Skip to content

Commit

Permalink
[#30] Properly sort prayer partners and clean up README.
Browse files Browse the repository at this point in the history
The new version of the data model simply contains an order property for prayer partner groups.
  • Loading branch information
nathanielbscout committed Mar 22, 2019
1 parent 02ffa8e commit 652ead9
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 13 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ As the app is still in development and there are a lot of small items on this li
### Important

- [ ] Update as needed to ensure compatibility with the new web app
- [ ] Fix the core data crash (results occasionally from the "Refresh event data now" button in settings) **--Claimed**
- [ ] Fix the core data crash (results occasionally from the "Refresh event data now" button in settings). @nathanielbscout took a look at this but couldn't reliably reproduce it - it is not fixed.
- [ ] Thoroughly test error display in different (re)load scenarios
- [ ] Ask server if there are new notifications past a certain date / fetch new notifications after a certain date (would likely require server changes)
- [ ] About screen. It is hooked up but has no meaningful content.
Expand Down Expand Up @@ -75,8 +75,8 @@ As the app is still in development and there are a lot of small items on this li
- [ ] Create an app widget (to give directions without being in the app; be cognizant of different map apps)
- [ ] Alternate event app text (in place of logo when it is missing, like in the android app)
- [ ] Swipe from edge of screen to open menu
- [ ] Improve managed object context usage (the data controller appears to violate Apple documentation's concurrency section by passing contexts between threads) **--Claimed**
- [ ] Make sure prayer partner groups have the same numbers as in the android app **--Claimed**
- [ ] Add some kind of order key to objects received in an array (not a dictionary) and update sorting to use that. **--Claimed**
- [ ] Improve managed object context usage (the data controller appears to violate Apple documentation's concurrency section by passing contexts between threads). @nathanielbscout took a look at this, but found that it only happens in a couple of places. It may cause some trouble, but there are not any clear issues with it.
- [x] Make sure prayer partner groups have the same numbers as in the android app.
- [x] Add some kind of order key to objects received in an array (not a dictionary) and update sorting to use that.
- [ ] Start refresh timer when opening the app to go off when it would have gone off, if the timer did not fire when the app was opened.
- [ ] Fix white bar above sidebar menu (very noticeable in app switcher)
4 changes: 3 additions & 1 deletion iOSEventApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
F1AF66712053292300B8820D /* ContactTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactTableViewCell.swift; sourceTree = "<group>"; };
F1AF66732053433B00B8820D /* NotificationTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationTableViewCell.swift; sourceTree = "<group>"; };
F1DB720A20A6655C00B30C54 /* iOSEventAppV2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = iOSEventAppV2.xcdatamodel; sourceTree = "<group>"; };
F1EE2DC422453D88006E9A1A /* iOSEventAppV3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = iOSEventAppV3.xcdatamodel; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -718,10 +719,11 @@
F149D6CE204DCE2F000DA81A /* iOSEventApp.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
F1EE2DC422453D88006E9A1A /* iOSEventAppV3.xcdatamodel */,
F1DB720A20A6655C00B30C54 /* iOSEventAppV2.xcdatamodel */,
F149D6CF204DCE2F000DA81A /* iOSEventApp.xcdatamodel */,
);
currentVersion = F1DB720A20A6655C00B30C54 /* iOSEventAppV2.xcdatamodel */;
currentVersion = F1EE2DC422453D88006E9A1A /* iOSEventAppV3.xcdatamodel */;
path = iOSEventApp.xcdatamodeld;
sourceTree = "<group>";
versionGroupType = wrapper.xcdatamodel;
Expand Down
9 changes: 7 additions & 2 deletions iOSEventApp/MainContainerViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ protocol TakesArrayData: AnyObject {
var dataArray: [Any]? { get set }
}

/// A workaround to allow sorting of data without having to do a separate if else for every data type.
/**
A workaround to allow sorting of data without having to do a separate if else for every data type.

It is important to sort for consistency across devices - although sometimes the order needs to be kept the same as in the JSON (e.g. prayer partners), which can be handled by passing the indices as strings.
*/
protocol IsComparable {
var compareString: String? { get }
}
Expand Down Expand Up @@ -168,7 +172,8 @@ class MainContainerViewController: UIViewController {
}

if let takesArrayData = vc as? TakesArrayData {
if let sortable = data as? [IsComparable] { // All objects in the array are the same type
// Sort the data if possible - it is simpler to sort here and not in each individual sidebar view controller.
if let sortable = data as? [IsComparable] {
takesArrayData.dataArray = sortable.sorted(by: { (obj1, obj2) -> Bool in
guard let str1 = obj1.compareString, let str2 = obj2.compareString else {
return false
Expand Down
11 changes: 7 additions & 4 deletions iOSEventApp/Model/DataController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ struct MalformedDataInformation: CustomStringConvertible {

DataController also starts the refresh timer in the refreshController, which it keeps a static reference to. The refresh timer triggers a reload of notifications.

Passing a context around different threads is against recommended practice. It will remain as is until changed.
The context should not be carelessly accessed from a thread different from the one it was created on. The only time a context is potentially passed between threads in this class is when a data task is performed - and it may be improperly handled in `loadNotificationsFromURL` or methods that call it, but since there are no clear issues right now it is being left alone.
*/
class DataController: NSObject {

Expand Down Expand Up @@ -150,6 +150,8 @@ class DataController: NSObject {
return
}

// This is in a performBackgroundTask block.
// I still don't think the context should *really* be passed like this, but there are no apparent issues with this at the moment.
self.loadNotificationsFromURL(context: context, url: URL(string: notificationsURLString)!) { (success, nErrors, newNotifications) in
if let additionalErrors = nErrors {
if case .unableToSave(_)? = additionalErrors.first {
Expand Down Expand Up @@ -391,10 +393,11 @@ extension DataController {
var sidebarKVPairs = [String: String]()
let groupDicts = partnerGroups as! [[String: Any]]

for obj in groupDicts {
// The prayer partners come in an array, and so the order should be preserved.
for (index, obj) in groupDicts.enumerated() {
if let partnerNames = obj["students"] {
if partnerNames is String {
groupstoCreate.append(["students": partnerNames])
groupstoCreate.append(["students": partnerNames, "order": Int16(index)])
}
else {
errors.append(.partiallyMalformed(MalformedDataInformation(objectName: "PrayerPartnerGroup", propertyName: "students", missingProperty: nil)))
Expand Down Expand Up @@ -638,7 +641,7 @@ extension DataController {
sidebarNum += 1

let pageIdentifier = key
// Ideally this wouldn't be hardcoded for an index of 0
// The event data has the sidebar info at the first index of each page array
guard let sidebarName = value[0][sidebarNameKey] as? String else {
errors.append(.partiallyMalformed(MalformedDataInformation(objectName: "Information Page \(pageNum)", propertyName: pageIdentifier, missingProperty: sidebarNameKey)))
continue
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
<plist version="1.0">
<dict>
<key>_XCCurrentVersionName</key>
<string>iOSEventAppV2.xcdatamodel</string>
<string>iOSEventAppV3.xcdatamodel</string>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="14460.32" systemVersion="18C54" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="Contact" representedClassName="Contact" syncable="YES" codeGenerationType="class">
<attribute name="address" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="phone" optional="YES" attributeType="String" syncable="YES"/>
</entity>
<entity name="ContactPageSection" representedClassName="ContactPageSection" syncable="YES" codeGenerationType="class">
<attribute name="content" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="header" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="id" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="key" optional="YES" attributeType="String" syncable="YES"/>
</entity>
<entity name="General" representedClassName="General" syncable="YES" codeGenerationType="class">
<attribute name="custom_time_zone" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="event_name" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="logo" optional="YES" attributeType="Binary" allowsExternalBinaryDataStorage="YES" syncable="YES"/>
<attribute name="notifications_url" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="refresh" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="refresh_expire" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="refresh_rate" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="remote_viewing" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="time_zone" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="version_num" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="welcome_message" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="year" optional="YES" attributeType="String" syncable="YES"/>
</entity>
<entity name="HousingUnit" representedClassName="HousingUnit" syncable="YES" codeGenerationType="class">
<attribute name="driver" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="hostName" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="students" optional="YES" attributeType="String" syncable="YES"/>
</entity>
<entity name="InformationPage" representedClassName="InformationPage" syncable="YES" codeGenerationType="class">
<attribute name="pageName" optional="YES" attributeType="String" syncable="YES"/>
<relationship name="infoNav" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SidebarAppearance" inverseName="infoPage" inverseEntity="SidebarAppearance" syncable="YES"/>
<relationship name="infoSections" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="InformationPageSection" inverseName="infoPage" inverseEntity="InformationPageSection" syncable="YES"/>
</entity>
<entity name="InformationPageSection" representedClassName="InformationPageSection" syncable="YES" codeGenerationType="class">
<attribute name="information" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="order" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="title" optional="YES" attributeType="String" syncable="YES"/>
<relationship name="infoPage" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="InformationPage" inverseName="infoSections" inverseEntity="InformationPage" syncable="YES"/>
</entity>
<entity name="Notification" representedClassName="Notification" syncable="YES" codeGenerationType="class">
<attribute name="body" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="date" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="notificationNumber" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="refresh" optional="YES" attributeType="Boolean" usesScalarValueType="YES" syncable="YES"/>
<attribute name="title" optional="YES" attributeType="String" syncable="YES"/>
</entity>
<entity name="PrayerPartnerGroup" representedClassName="PrayerPartnerGroup" syncable="YES" codeGenerationType="class">
<attribute name="order" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="students" optional="YES" attributeType="String" syncable="YES"/>
</entity>
<entity name="ScheduleDay" representedClassName="ScheduleDay" syncable="YES" codeGenerationType="class">
<attribute name="date" optional="YES" attributeType="String" syncable="YES"/>
<relationship name="items" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="ScheduleItem" inverseName="day" inverseEntity="ScheduleItem" syncable="YES"/>
</entity>
<entity name="ScheduleItem" representedClassName="ScheduleItem" syncable="YES" codeGenerationType="class">
<attribute name="category" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="itemDescription" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="length" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="location" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="startTime" optional="YES" attributeType="String" syncable="YES"/>
<relationship name="day" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="ScheduleDay" inverseName="items" inverseEntity="ScheduleDay" syncable="YES"/>
</entity>
<entity name="SidebarAppearance" representedClassName="SidebarAppearance" syncable="YES" codeGenerationType="class">
<attribute name="category" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="icon" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="nav" attributeType="String" syncable="YES"/>
<attribute name="optionalIdentifier" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="order" attributeType="String" syncable="YES"/>
<relationship name="infoPage" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="InformationPage" inverseName="infoNav" inverseEntity="InformationPage" syncable="YES"/>
</entity>
<entity name="Theme" representedClassName="Theme" syncable="YES" codeGenerationType="class">
<attribute name="themeName" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="themeValue" optional="YES" attributeType="String" syncable="YES"/>
</entity>
<elements>
<element name="Contact" positionX="27" positionY="126" width="128" height="90"/>
<element name="ContactPageSection" positionX="-9" positionY="90" width="128" height="105"/>
<element name="General" positionX="18" positionY="63" width="128" height="225"/>
<element name="HousingUnit" positionX="18" positionY="63" width="128" height="90"/>
<element name="InformationPage" positionX="18" positionY="126" width="128" height="90"/>
<element name="InformationPageSection" positionX="27" positionY="135" width="128" height="105"/>
<element name="Notification" positionX="9" positionY="126" width="128" height="120"/>
<element name="PrayerPartnerGroup" positionX="0" positionY="45" width="128" height="75"/>
<element name="ScheduleDay" positionX="-9" positionY="81" width="128" height="75"/>
<element name="ScheduleItem" positionX="0" positionY="90" width="128" height="135"/>
<element name="SidebarAppearance" positionX="9" positionY="54" width="128" height="135"/>
<element name="Theme" positionX="36" positionY="135" width="128" height="75"/>
</elements>
</model>
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import UIKit

extension PrayerPartnerGroup: IsComparable {
var compareString: String? {
return students
return String(order)
}
}

Expand Down

0 comments on commit 652ead9

Please sign in to comment.