diff --git a/Anyway.xcodeproj/project.pbxproj b/Anyway.xcodeproj/project.pbxproj index 5dc028b..5264631 100644 --- a/Anyway.xcodeproj/project.pbxproj +++ b/Anyway.xcodeproj/project.pbxproj @@ -431,7 +431,6 @@ 30D6E5AA1A92639500337FDB /* Resources */, 30673C631C1F3D3800CEB5FB /* Run Script | Set Build To "DEVELOPMENT" */, EE6970D3164DFFA9D86A01CE /* [CP] Embed Pods Frameworks */, - 37080D3EA49B66589C1F3E91 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -455,7 +454,7 @@ TargetAttributes = { 30D6E5AB1A92639500337FDB = { CreatedOnToolsVersion = 6.1.1; - DevelopmentTeam = 85JPBD89LM; + DevelopmentTeam = KMG627KSFF; LastSwiftMigration = 0820; ProvisioningStyle = Automatic; }; @@ -524,30 +523,26 @@ shellPath = /bin/sh; shellScript = "/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion DEVELOPMENT\" \"${PROJECT_DIR}/${INFOPLIST_FILE}\""; }; - 37080D3EA49B66589C1F3E91 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Anyway/Pods-Anyway-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; EE6970D3164DFFA9D86A01CE /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${SRCROOT}/Pods/Target Support Files/Pods-Anyway/Pods-Anyway-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework", + "${BUILT_PRODUCTS_DIR}/Eureka/Eureka.framework", + "${BUILT_PRODUCTS_DIR}/Realm/Realm.framework", + "${BUILT_PRODUCTS_DIR}/RealmSwift/RealmSwift.framework", + "${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Eureka.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Realm.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RealmSwift.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyJSON.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -560,13 +555,16 @@ files = ( ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Anyway-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -699,6 +697,7 @@ MTL_ENABLE_DEBUG_INFO = NO; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -712,6 +711,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEVELOPMENT_TEAM = KMG627KSFF; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/build/Debug-iphoneos", @@ -724,7 +724,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; SWIFT_OBJC_BRIDGING_HEADER = "Anyway/Anyway-Bridging-Header.h"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -763,7 +763,8 @@ IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -777,7 +778,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = KMG627KSFF; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/build/Debug-iphoneos", @@ -790,7 +791,7 @@ PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Anyway/Anyway-Bridging-Header.h"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Release; }; diff --git a/Anyway/DetailCells.swift b/Anyway/DetailCells.swift index 2ca6b53..358c64a 100644 --- a/Anyway/DetailCells.swift +++ b/Anyway/DetailCells.swift @@ -202,14 +202,14 @@ private struct StaticData { case (2, let i): return marker?.roadConditionData.safeRetrieveElement(i)?.1 ?? "" case (3, 1): return "\(data.created.longDate), \(data.created.shortTime)" - case (3, 2): return Localization.sug_YOM[data.dayType] ?? "" + case (3, 2): return Localization.SUG_YOM[data.dayType] ?? "" case (3, 3): return data.address case (4, let i): return fieldValue(i, rawInfos: persons) case (5, let i): return fieldValue(i, rawInfos: vehicles) - case (6, 1): return Localization.status_IGUN[data.intactness] ?? "" //TODO: is right param? - case (6, 2): return Localization.yehida[data.unit] ?? "" + case (6, 1): return Localization.STATUS_IGUN[data.intactness] ?? "" //TODO: is right param? + case (6, 2): return Localization.YEHIDA[data.unit] ?? "" default: return "" } diff --git a/Anyway/DetailViewController.swift b/Anyway/DetailViewController.swift index 7e8a3d1..8c68ab2 100644 --- a/Anyway/DetailViewController.swift +++ b/Anyway/DetailViewController.swift @@ -49,9 +49,9 @@ class DetailViewController: UIViewController, UITableViewDelegate, UITableViewDa func handleMarkerChanged() { guard let marker = detailData else {return} - network.getMarkerDetails(markerId: marker.id) { [weak self] in - self?.persons = $0.0 - self?.vehicles = $0.1 + network.getMarkerDetails(markerId: marker.id) { [weak self] personList,vehicleList in + self?.persons = personList ;//$0.0 + self?.vehicles = vehicleList ;//$0.1 self?.tableView.reloadData() } diff --git a/Anyway/HistoryPosition.swift b/Anyway/HistoryPosition.swift index ca07e68..1a6cccc 100644 --- a/Anyway/HistoryPosition.swift +++ b/Anyway/HistoryPosition.swift @@ -11,8 +11,8 @@ import RealmSwift class HistoryPosition: Object { - dynamic var locationData: Data? - dynamic var locationPLacemark: Data? + @objc dynamic var locationData: Data? + @objc dynamic var locationPLacemark: Data? var markers = List() } diff --git a/Anyway/Images.xcassets/AppIcon.appiconset/Contents.json b/Anyway/Images.xcassets/AppIcon.appiconset/Contents.json index b22f5b6..3581572 100644 --- a/Anyway/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/Anyway/Images.xcassets/AppIcon.appiconset/Contents.json @@ -97,6 +97,11 @@ "idiom" : "ipad", "filename" : "Icon-83.5@2x.png", "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" } ], "info" : { diff --git a/Anyway/Localization.swift b/Anyway/Localization.swift index 671361b..9d3e39c 100644 --- a/Anyway/Localization.swift +++ b/Anyway/Localization.swift @@ -26,20 +26,47 @@ import Foundation enum Localization { // Road Conditions - case sug_DERECH, yehida, sug_YOM, humrat_TEUNA, sug_TEUNA, - zurat_DEREH, had_MASLUL, rav_MASLUL, mehirut_MUTERET, - tkinut, rohav, simun_TIMRUR, teura, bakara, mezeg_AVIR, - pne_KVISH, sug_EZEM, merhak_EZEM, lo_HAZA, ofen_HAZIYA, - mekom_HAZIYA, kivun_HAZIYA, status_IGUN + case SUG_DERECH, + YEHIDA, + SUG_YOM, + HUMRAT_TEUNA, + SUG_TEUNA, + ZURAT_DEREH, + HAD_MASLUL, + RAV_MASLUL, + MHIRUT_MUTERET, + TKINUT, + ROHAV, + SIMUN_TIMRUR, + TEURA, + BAKARA, + MEZEG_AVIR, + PNE_KVISH, + SUG_EZEM, + MERHAK_EZEM, + LO_HAZA, + OFEN_HAZIYA, + MEKOM_HAZIYA, + KIVUN_HAZIYA, + STATUS_IGUN // Vehicle Description - case matzav_REHEV, shiyuh_REHEV_LMS, sug_REHEV_LMS + case MATSAV_REHEV, + SHIYUH_REHEV_LMS, + SUG_REHEV_LMS // Involved Person Description - case sug_MEORAV, min, emzae_BETIHUT, - humrat_PGIA, sug_NIFGA_LMS, peulat_NIFGA_LMS, - pazua_USHPAZ, madad_RAFUI, yaad_SHIHRUR, - shimush_BE_AVIZAREY_BETIHOT, ptira_MEUHERET + case SUG_MEORAV, + MIN, + EMZAE_BETIHUT, + HUMRAT_PGIA, + SUG_NIFGA_LMS, + PEULAT_NIFGA_LMS, + PAZUA_USHPAZ, + MADAD_RAFUI, + YAAD_SHIHRUR, + SHIMUSH_BE_AVIZAREY_BETIHOT, + PTIRA_MEUHERET subscript(val: Int) -> String? { diff --git a/Anyway/Marker.swift b/Anyway/Marker.swift index f7dc01d..eecea04 100644 --- a/Anyway/Marker.swift +++ b/Anyway/Marker.swift @@ -12,46 +12,47 @@ import RealmSwift class Marker: Object, MarkerAnnotation { - dynamic var coordinateLat: Double = 0 - dynamic var coordinateLon: Double = 0 - dynamic var address: String = "" - dynamic var descriptionContent: String = "" - dynamic var titleAccident: String = "" - dynamic var created: Date = Date(timeIntervalSince1970: 0) + @objc dynamic var coordinateLat: Double = 0 + @objc dynamic var coordinateLon: Double = 0 + + @objc dynamic var address: String = "" + @objc dynamic var descriptionContent: String = "" + @objc dynamic var titleAccident: String = "" + @objc dynamic var created: Date = Date(timeIntervalSince1970: 0) var followers: [AnyObject] = [] - dynamic var following: Bool = false - dynamic var id: Int = 0 - dynamic var locationAccuracy: Int = 0 - dynamic var severity: Int = 0 - dynamic var subtype: Int = 0 - dynamic var type: Int = 0 - dynamic var user: String = "" - - dynamic var roadShape: Int = -1 - dynamic var cross_mode: Int = -1 - dynamic var secondaryStreet: String = "" - dynamic var cross_location: Int = -1 - dynamic var one_lane: Int = -1 - dynamic var speed_limit: Int = -1 - dynamic var weather: Int = -1 - dynamic var provider_code: Int = -1 - dynamic var road_object: Int = -1 - dynamic var didnt_cross: Int = -1 - dynamic var object_distance: Int = -1 - dynamic var road_sign: Int = -1 - dynamic var intactness: Int = -1 - dynamic var junction: String = "" - dynamic var road_control: Int = -1 - dynamic var road_light: Int = -1 - dynamic var multi_lane: Int = -1 - dynamic var dayType: Int = -1 - dynamic var unit: Int = -1 - dynamic var road_width: Int = -1 - dynamic var cross_direction: Int = -1 - dynamic var roadType: Int = -1 - dynamic var road_surface: Int = -1 - dynamic var mainStreet: String = "" + @objc dynamic var following: Bool = false + @objc dynamic var id: Int = 0 + @objc dynamic var locationAccuracy: Int = 0 + @objc dynamic var severity: Int = 0 + @objc dynamic var subtype: Int = 0 + @objc dynamic var type: Int = 0 + @objc dynamic var user: String = "" + + @objc dynamic var roadShape: Int = -1 + @objc dynamic var cross_mode: Int = -1 + @objc dynamic var secondaryStreet: String = "" + @objc dynamic var cross_location: Int = -1 + @objc dynamic var one_lane: Int = -1 + @objc dynamic var speed_limit: Int = -1 + @objc dynamic var weather: Int = -1 + @objc dynamic var provider_code: Int = -1 + @objc dynamic var road_object: Int = -1 + @objc dynamic var didnt_cross: Int = -1 + @objc dynamic var object_distance: Int = -1 + @objc dynamic var road_sign: Int = -1 + @objc dynamic var intactness: Int = -1 + @objc dynamic var junction: String = "" + @objc dynamic var road_control: Int = -1 + @objc dynamic var road_light: Int = -1 + @objc dynamic var multi_lane: Int = -1 + @objc dynamic var dayType: Int = -1 + @objc dynamic var unit: Int = -1 + @objc dynamic var road_width: Int = -1 + @objc dynamic var cross_direction: Int = -1 + @objc dynamic var roadType: Int = -1 + @objc dynamic var road_surface: Int = -1 + @objc dynamic var mainStreet: String = "" /// Properties ignored by Realm @@ -104,16 +105,16 @@ extension Marker: PairsData { var roadConditionData: [(Title, Detail)] { return [ - Marker.pair(forType: .sug_DERECH, value: roadType), - Marker.pair(forType: .zurat_DEREH, value: roadShape), - Marker.pair(forType: .had_MASLUL, value: one_lane), - Marker.pair(forType: .mehirut_MUTERET, value: speed_limit), - Marker.pair(forType: .tkinut, value: intactness), - Marker.pair(forType: .rohav, value: road_width), - Marker.pair(forType: .simun_TIMRUR, value: road_sign), - Marker.pair(forType: .teura, value: road_light), - Marker.pair(forType: .bakara, value: road_control), - Marker.pair(forType: .mezeg_AVIR, value: weather) + Marker.pair(forType: .SUG_DERECH, value: roadType), + Marker.pair(forType: .ZURAT_DEREH, value: roadShape), + Marker.pair(forType: .HAD_MASLUL, value: one_lane), + Marker.pair(forType: .MHIRUT_MUTERET, value: speed_limit), + Marker.pair(forType: .TKINUT, value: intactness), + Marker.pair(forType: .ROHAV, value: road_width), + Marker.pair(forType: .SIMUN_TIMRUR, value: road_sign), + Marker.pair(forType: .TEURA, value: road_light), + Marker.pair(forType: .BAKARA, value: road_control), + Marker.pair(forType: .MEZEG_AVIR, value: weather) ].flatMap{ $0 } } @@ -129,15 +130,15 @@ extension Marker: VisualMarker { //MARK: Localized Info var localizedSubtype: String { - return Localization.sug_TEUNA[subtype] ?? "" + return Localization.SUG_TEUNA[subtype] ?? "" } var localizedSeverity: String { - return Localization.humrat_TEUNA[severity] ?? "" + return Localization.HUMRAT_TEUNA[severity] ?? "" } var localizedAccuracy: String { - return Localization.status_IGUN[locationAccuracy] ?? "" + return Localization.STATUS_IGUN[locationAccuracy] ?? "" } var color: UIColor { diff --git a/Anyway/MarkerViews.swift b/Anyway/MarkerViews.swift index 47c7f48..46dcef1 100644 --- a/Anyway/MarkerViews.swift +++ b/Anyway/MarkerViews.swift @@ -105,7 +105,7 @@ class IconPinView: UIView { didSet { // whenever the color changes > change // the tint for any subview - for i in subviews.flatMap({ $0 as? UIImageView }) { + for i in subviews.compactMap({ $0 as? UIImageView }) { i.tintColor = color } } @@ -164,9 +164,13 @@ class MarkerView: MKAnnotationView { self.init(annotation: marker, reuseIdentifier: reuseIdentifier) isEnabled = true + //rightCalloutAccessoryView = UIButton(type: .detailDisclosure) as UIView + + rightCalloutAccessoryView = UIButton.init(type: UIButtonType.detailDisclosure) + canShowCallout = true - rightCalloutAccessoryView = UIButton(type: .detailDisclosure) as UIView + isUserInteractionEnabled = true setupIcon(marker) } diff --git a/Anyway/Person.swift b/Anyway/Person.swift index c232137..7674744 100644 --- a/Anyway/Person.swift +++ b/Anyway/Person.swift @@ -14,18 +14,18 @@ extension Person: PairsData { static func buildPersonDescriptionData(_ unparsedInfo: [(String, String)]) -> [(Title, Detail)] { return unparsedInfo.map{ (rawTitle, rawValue) in switch rawTitle { - case "SUG_MEORAV": return pair(forType: .sug_MEORAV, value: Int(rawValue) ?? -1) - case "MIN": return pair(forType: .min, value: Int(rawValue) ?? -1) - case "SUG_REHEV_NASA_LMS": return pair(forType: .sug_REHEV_LMS, value: Int(rawValue) ?? -1) - case "EMZAE_BETIHUT": return pair(forType: .emzae_BETIHUT, value: Int(rawValue) ?? -1) - case "HUMRAT_PGIA": return pair(forType: .humrat_PGIA, value: Int(rawValue) ?? -1) - case "SUG_NIFGA_LMS": return pair(forType: .sug_NIFGA_LMS, value: Int(rawValue) ?? -1) - case "PEULAT_NIFGA_LMS": return pair(forType: .peulat_NIFGA_LMS, value: Int(rawValue) ?? -1) - case "PAZUA_USHPAZ": return pair(forType: .pazua_USHPAZ, value: Int(rawValue) ?? -1) - case "MADAD_RAFUI": return pair(forType: .madad_RAFUI, value: Int(rawValue) ?? -1) - case "YAAD_SHIHRUR": return pair(forType: .yaad_SHIHRUR, value: Int(rawValue) ?? -1) - case "SHIMUSH_BE_AVIZAREY_BETIHOT": return pair(forType: .shimush_BE_AVIZAREY_BETIHOT, value: Int(rawValue) ?? -1) - case "PTIRA_MEUHERET": return pair(forType: .ptira_MEUHERET, value: Int(rawValue) ?? -1) + case "SUG_MEORAV": return pair(forType: .SUG_MEORAV, value: Int(rawValue) ?? -1) + case "MIN": return pair(forType: .MIN, value: Int(rawValue) ?? -1) + case "SUG_REHEV_NASA_LMS": return pair(forType: .SUG_REHEV_LMS, value: Int(rawValue) ?? -1) + case "EMZAE_BETIHUT": return pair(forType: .EMZAE_BETIHUT, value: Int(rawValue) ?? -1) + case "HUMRAT_PGIA": return pair(forType: .HUMRAT_PGIA, value: Int(rawValue) ?? -1) + case "SUG_NIFGA_LMS": return pair(forType: .SUG_NIFGA_LMS, value: Int(rawValue) ?? -1) + case "PEULAT_NIFGA_LMS": return pair(forType: .PEULAT_NIFGA_LMS, value: Int(rawValue) ?? -1) + case "PAZUA_USHPAZ": return pair(forType: .PAZUA_USHPAZ, value: Int(rawValue) ?? -1) + case "MADAD_RAFUI": return pair(forType: .MADAD_RAFUI, value: Int(rawValue) ?? -1) + case "YAAD_SHIHRUR": return pair(forType: .YAAD_SHIHRUR, value: Int(rawValue) ?? -1) + case "SHIMUSH_BE_AVIZAREY_BETIHOT": return pair(forType: .SHIMUSH_BE_AVIZAREY_BETIHOT, value: Int(rawValue) ?? -1) + case "PTIRA_MEUHERET": return pair(forType: .PTIRA_MEUHERET, value: Int(rawValue) ?? -1) default: return (local(rawTitle), rawValue) } diff --git a/Anyway/Utilities.swift b/Anyway/Utilities.swift index 2ea8b33..97b9125 100644 --- a/Anyway/Utilities.swift +++ b/Anyway/Utilities.swift @@ -318,10 +318,10 @@ extension UIImage { context?.setFillColor(backColor.cgColor) context?.fill(CGRect(x: 0, y: 0, width: size.width, height: size.height)) - let dict = [NSFontAttributeName: font, NSForegroundColorAttributeName: textColor] + let dict = [kCTFontAttributeName: font, kCTForegroundColorAttributeName: textColor] let nsInitials = initials as NSString - let textSize = nsInitials.size(attributes: dict) - nsInitials.draw(in: CGRect(x: r - textSize.width / 2, y: r - font.lineHeight / 2, width: size.width, height: size.height), withAttributes: dict) + let textSize = nsInitials.size(withAttributes: dict as [NSAttributedStringKey : Any]) + nsInitials.draw(in: CGRect(x: r - textSize.width / 2, y: r - font.lineHeight / 2, width: size.width, height: size.height), withAttributes: dict as [NSAttributedStringKey : Any]) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() @@ -403,11 +403,11 @@ extension String { return numOfMatches > 0 } - - func attributedStringFromHTMLString(_ overrideFont: Bool = false, color: UIColor = UIColor.white) -> NSAttributedString { + + /* func attributedStringFromHTMLString(_ overrideFont: Bool = false, color: UIColor = UIColor.white) -> NSAttributedString { let options = [ - NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, - NSCharacterEncodingDocumentAttribute: String.Encoding.utf8 + //NSDocumentTypeDocumentOption: NSHTMLTextDocumentType, + NSAttributedString.DocumentReadingOptionKey.characterEncoding.rawValue: String.Encoding.utf8 ] as [String : Any] var atStr: NSMutableAttributedString? @@ -418,12 +418,12 @@ extension String { { let range = NSMakeRange(0, NSString(string: str.string).length) - str.addAttribute(NSForegroundColorAttributeName, value: color, range: range) - + str.addAttribute(kCTForegroundColorAttributeName as NSAttributedStringKey, value: color, range: range) + if overrideFont { str.addAttribute(NSFontAttributeName, value: UIFont.preferredFont(forTextStyle: UIFontTextStyle.body), range:range) } - + atStr = str } @@ -439,7 +439,8 @@ extension String { } return NSMutableAttributedString(string: "") - } + }*/ + func stringByTrimmingHTMLTags() -> String { return self.replacingOccurrences(of: "<[^>]+>", with: "", options: .regularExpression, range: nil) @@ -511,7 +512,7 @@ extension String { extension NSMutableAttributedString { public func addAttribute(_ name: String, value: AnyObject, ranges: [NSRange]) { for r in ranges { - addAttribute(name, value: value, range: r) + addAttribute(NSAttributedStringKey(rawValue: name), value: value, range: r) } } } diff --git a/Anyway/Vehicle.swift b/Anyway/Vehicle.swift index 8d53658..5b98193 100644 --- a/Anyway/Vehicle.swift +++ b/Anyway/Vehicle.swift @@ -23,9 +23,9 @@ extension Vehicle: PairsData { static func buildVehicleDescriptionData(_ unparsedInfo: [(String, String)]) -> [(Title, Detail)] { return unparsedInfo.map{ (rawTitle, rawValue) in switch rawTitle { - case "MATZAV_REHEV": return pair(forType: .matzav_REHEV, value: Int(rawValue) ?? -1) - case "SHIYUH_REHEV_LMS": return pair(forType: .shiyuh_REHEV_LMS, value: Int(rawValue) ?? -1) - case "SUG_REHEV_LMS": return pair(forType: .sug_REHEV_LMS, value: Int(rawValue) ?? -1) + case "MATZAV_REHEV": return pair(forType: .MATSAV_REHEV, value: Int(rawValue) ?? -1) + case "SHIYUH_REHEV_LMS": return pair(forType: .SHIYUH_REHEV_LMS, value: Int(rawValue) ?? -1) + case "SUG_REHEV_LMS": return pair(forType: .SUG_REHEV_LMS, value: Int(rawValue) ?? -1) default: return (local(rawTitle), rawValue) } }.flatMap{ $0 } diff --git a/Anyway/ViewController+MapView.swift b/Anyway/ViewController+MapView.swift index a6b07b8..bd2ce38 100644 --- a/Anyway/ViewController+MapView.swift +++ b/Anyway/ViewController+MapView.swift @@ -15,6 +15,7 @@ import Foundation */ extension ViewController: MKMapViewDelegate { + /** Figure out wether the map was moved due to user interaction or not. @@ -43,6 +44,7 @@ extension ViewController: MKMapViewDelegate { let distance = CLLocation.distance(from: lastRegion.center, to: mapView.region.center) print("distance: \(distance)") + //?? YIGAL what is this ? if distance > 50 { updateInfoIfPossible(mapView, filterChanged:false) } @@ -84,8 +86,7 @@ extension ViewController: MKMapViewDelegate { return mView } return MarkerView(marker: marker) - - } + } if let markerGroup = annotation as? MarkerGroup { if let mView = mapView.dequeueReusableAnnotationView(withIdentifier: markerGroupReuseIdentifierDefault) as? MarkerGroupView { @@ -105,6 +106,7 @@ extension ViewController: MKMapViewDelegate { Opens the screen to show more details about the accident. */ func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) { + let sbID = DetailViewController.storyboardId @@ -126,6 +128,7 @@ extension ViewController: MKMapViewDelegate { } } + /** Handles the event of user selecting an annotation. @@ -138,6 +141,7 @@ extension ViewController: MKMapViewDelegate { */ func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) { // handle a marker group + print("mapView:didSelect") if let groupView = view as? MarkerGroupView { // remove the center, "fake", annotation diff --git a/Anyway/ViewController.swift b/Anyway/ViewController.swift index 61ebf8e..e779961 100644 --- a/Anyway/ViewController.swift +++ b/Anyway/ViewController.swift @@ -75,6 +75,8 @@ class ViewController: UIViewController, CLLocationManagerDelegate { override func viewDidLoad() { super.viewDidLoad() + print("viewDidLoad") + map.delegate = self map.clusterSize = 0.1 map.minimumAnnotationCountPerCluster = 4 diff --git a/Podfile b/Podfile index 106b967..002920b 100644 --- a/Podfile +++ b/Podfile @@ -5,6 +5,6 @@ use_frameworks! target 'Anyway' do pod 'Alamofire' pod 'SwiftyJSON' - pod 'Eureka', '~> 2.0' + pod 'Eureka', '~> 4.2.0' pod 'RealmSwift' end diff --git a/Podfile.lock b/Podfile.lock index 0581254..e32ff83 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,26 +1,34 @@ PODS: - - Alamofire (4.4.0) - - Eureka (2.0.1) - - Realm (2.4.3): - - Realm/Headers (= 2.4.3) - - Realm/Headers (2.4.3) - - RealmSwift (2.4.3): - - Realm (= 2.4.3) - - SwiftyJSON (3.1.4) + - Alamofire (4.7.3) + - Eureka (4.2.0) + - Realm (3.7.6): + - Realm/Headers (= 3.7.6) + - Realm/Headers (3.7.6) + - RealmSwift (3.7.6): + - Realm (= 3.7.6) + - SwiftyJSON (4.1.0) DEPENDENCIES: - Alamofire - - Eureka (~> 2.0) + - Eureka (~> 4.2.0) - RealmSwift - SwiftyJSON +SPEC REPOS: + https://github.com/CocoaPods/Specs.git: + - Alamofire + - Eureka + - Realm + - RealmSwift + - SwiftyJSON + SPEC CHECKSUMS: - Alamofire: dc44b1600b800eb63da6a19039a0083d62a6a62d - Eureka: ae837bb1dfae0c07bc1bda7aeef87876eeb537c2 - Realm: e08d60b0048ccccd3949c8c28a92c4db5712d7e9 - RealmSwift: 30d49739ccb3385d201242736ca4dda56aec9dfc - SwiftyJSON: c2842d878f95482ffceec5709abc3d05680c0220 + Alamofire: c7287b6e5d7da964a70935e5db17046b7fde6568 + Eureka: 0748c7bbb2560130e43c7bfa83e99c98854a1f42 + Realm: 9eaecad54712d6246d08ba34c10f354e4715d7d3 + RealmSwift: 1fe08b4ebaeeaacf17f9ba0343f7b25c9fc31fa6 + SwiftyJSON: c29297daf073d2aa016295d5809cdd68045c39b3 -PODFILE CHECKSUM: 8d33131f567d5e8d57a6d7d0d5ded13deac16030 +PODFILE CHECKSUM: ad2c1cd3d3c073e25f16eef71b60c2ff816f6511 -COCOAPODS: 1.2.0.beta.1 +COCOAPODS: 1.5.0 diff --git a/Pods/Alamofire/LICENSE b/Pods/Alamofire/LICENSE index 4cfbf72..2ec3cb1 100644 --- a/Pods/Alamofire/LICENSE +++ b/Pods/Alamofire/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) +Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Pods/Alamofire/README.md b/Pods/Alamofire/README.md index 12ea4c7..0208252 100644 --- a/Pods/Alamofire/README.md +++ b/Pods/Alamofire/README.md @@ -1,10 +1,10 @@ -![Alamofire: Elegant Networking in Swift](https://raw.githubusercontent.com/Alamofire/Alamofire/assets/alamofire.png) +![Alamofire: Elegant Networking in Swift](https://raw.githubusercontent.com/Alamofire/Alamofire/master/alamofire.png) [![Build Status](https://travis-ci.org/Alamofire/Alamofire.svg?branch=master)](https://travis-ci.org/Alamofire/Alamofire) [![CocoaPods Compatible](https://img.shields.io/cocoapods/v/Alamofire.svg)](https://img.shields.io/cocoapods/v/Alamofire.svg) [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) -[![Platform](https://img.shields.io/cocoapods/p/Alamofire.svg?style=flat)](http://cocoadocs.org/docsets/Alamofire) -[![Twitter](https://img.shields.io/badge/twitter-@AlamofireSF-blue.svg?style=flat)](http://twitter.com/AlamofireSF) +[![Platform](https://img.shields.io/cocoapods/p/Alamofire.svg?style=flat)](https://alamofire.github.io/Alamofire) +[![Twitter](https://img.shields.io/badge/twitter-@AlamofireSF-blue.svg?style=flat)](https://twitter.com/AlamofireSF) [![Gitter](https://badges.gitter.im/Alamofire/Alamofire.svg)](https://gitter.im/Alamofire/Alamofire?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) Alamofire is an HTTP networking library written in Swift. @@ -15,16 +15,16 @@ Alamofire is an HTTP networking library written in Swift. - [Migration Guides](#migration-guides) - [Communication](#communication) - [Installation](#installation) -- [Usage](#usage) - - **Intro -** [Making a Request](#making-a-request), [Response Handling](#response-handling), [Response Validation](#response-validation), [Response Caching](#response-caching) - - **HTTP -** [HTTP Methods](#http-methods), [Parameter Encoding](#parameter-encoding), [HTTP Headers](#http-headers), [Authentication](#authentication) - - **Large Data -** [Downloading Data to a File](#downloading-data-to-a-file), [Uploading Data to a Server](#uploading-data-to-a-server) - - **Tools -** [Statistical Metrics](#statistical-metrics), [cURL Command Output](#curl-command-output) -- [Advanced Usage](#advanced-usage) - - **URL Session -** [Session Manager](#session-manager), [Session Delegate](#session-delegate), [Request](#request) - - **Routing -** [Routing Requests](#routing-requests), [Adapting and Retrying Requests](#adapting-and-retrying-requests) - - **Model Objects -** [Custom Response Serialization](#custom-response-serialization) - - **Connection -** [Security](#security), [Network Reachability](#network-reachability) +- [Usage](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md) + - **Intro -** [Making a Request](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#making-a-request), [Response Handling](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-handling), [Response Validation](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-validation), [Response Caching](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-caching) + - **HTTP -** [HTTP Methods](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#http-methods), [Parameter Encoding](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#parameter-encoding), [HTTP Headers](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#http-headers), [Authentication](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#authentication) + - **Large Data -** [Downloading Data to a File](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#downloading-data-to-a-file), [Uploading Data to a Server](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#uploading-data-to-a-server) + - **Tools -** [Statistical Metrics](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#statistical-metrics), [cURL Command Output](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#curl-command-output) +- [Advanced Usage](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md) + - **URL Session -** [Session Manager](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#session-manager), [Session Delegate](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#session-delegate), [Request](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#request) + - **Routing -** [Routing Requests](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#routing-requests), [Adapting and Retrying Requests](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#adapting-and-retrying-requests) + - **Model Objects -** [Custom Response Serialization](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#custom-response-serialization) + - **Connection -** [Security](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#security), [Network Reachability](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#network-reachability) - [Open Radars](#open-radars) - [FAQ](#faq) - [Credits](#credits) @@ -45,7 +45,7 @@ Alamofire is an HTTP networking library written in Swift. - [x] TLS Certificate and Public Key Pinning - [x] Network Reachability - [x] Comprehensive Unit and Integration Test Coverage -- [x] [Complete Documentation](http://cocoadocs.org/docsets/Alamofire) +- [x] [Complete Documentation](https://alamofire.github.io/Alamofire) ## Component Libraries @@ -57,8 +57,8 @@ In order to keep Alamofire focused specifically on core networking implementatio ## Requirements - iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+ -- Xcode 8.1+ -- Swift 3.0+ +- Xcode 8.3+ +- Swift 3.1+ ## Migration Guides @@ -68,8 +68,8 @@ In order to keep Alamofire focused specifically on core networking implementatio ## Communication -- If you **need help**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/alamofire). (Tag 'alamofire') -- If you'd like to **ask a general question**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/alamofire). +- If you **need help**, use [Stack Overflow](https://stackoverflow.com/questions/tagged/alamofire). (Tag 'alamofire') +- If you'd like to **ask a general question**, use [Stack Overflow](https://stackoverflow.com/questions/tagged/alamofire). - If you **found a bug**, open an issue. - If you **have a feature request**, open an issue. - If you **want to contribute**, submit a pull request. @@ -78,13 +78,13 @@ In order to keep Alamofire focused specifically on core networking implementatio ### CocoaPods -[CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects. You can install it with the following command: +[CocoaPods](https://cocoapods.org) is a dependency manager for Cocoa projects. You can install it with the following command: ```bash $ gem install cocoapods ``` -> CocoaPods 1.1.0+ is required to build Alamofire 4.0.0+. +> CocoaPods 1.1+ is required to build Alamofire 4.0+. To integrate Alamofire into your Xcode project using CocoaPods, specify it in your `Podfile`: @@ -94,7 +94,7 @@ platform :ios, '10.0' use_frameworks! target '' do - pod 'Alamofire', '~> 4.4' + pod 'Alamofire', '~> 4.7' end ``` @@ -108,7 +108,7 @@ $ pod install [Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. -You can install Carthage with [Homebrew](http://brew.sh/) using the following command: +You can install Carthage with [Homebrew](https://brew.sh/) using the following command: ```bash $ brew update @@ -118,7 +118,7 @@ $ brew install carthage To integrate Alamofire into your Xcode project using Carthage, specify it in your `Cartfile`: ```ogdl -github "Alamofire/Alamofire" ~> 4.4 +github "Alamofire/Alamofire" ~> 4.7 ``` Run `carthage update` to build the framework and drag the built `Alamofire.framework` into your Xcode project. @@ -129,29 +129,39 @@ The [Swift Package Manager](https://swift.org/package-manager/) is a tool for au Once you have your Swift package set up, adding Alamofire as a dependency is as easy as adding it to the `dependencies` value of your `Package.swift`. +#### Swift 3 + ```swift dependencies: [ .Package(url: "https://github.com/Alamofire/Alamofire.git", majorVersion: 4) ] ``` +#### Swift 4 + +```swift +dependencies: [ + .package(url: "https://github.com/Alamofire/Alamofire.git", from: "4.0.0") +] +``` + ### Manually -If you prefer not to use either of the aforementioned dependency managers, you can integrate Alamofire into your project manually. +If you prefer not to use any of the aforementioned dependency managers, you can integrate Alamofire into your project manually. #### Embedded Framework - Open up Terminal, `cd` into your top-level project directory, and run the following command "if" your project is not initialized as a git repository: ```bash -$ git init -``` + $ git init + ``` -- Add Alamofire as a git [submodule](http://git-scm.com/docs/git-submodule) by running the following command: +- Add Alamofire as a git [submodule](https://git-scm.com/docs/git-submodule) by running the following command: ```bash -$ git submodule add https://github.com/Alamofire/Alamofire.git -``` + $ git submodule add https://github.com/Alamofire/Alamofire.git + ``` - Open the new `Alamofire` folder, and drag the `Alamofire.xcodeproj` into the Project Navigator of your application's Xcode project. @@ -173,1660 +183,33 @@ $ git submodule add https://github.com/Alamofire/Alamofire.git > The `Alamofire.framework` is automagically added as a target dependency, linked framework and embedded framework in a copy files build phase which is all you need to build on the simulator and a device. ---- - -## Usage - -### Making a Request - -```swift -import Alamofire - -Alamofire.request("https://httpbin.org/get") -``` - -### Response Handling - -Handling the `Response` of a `Request` made in Alamofire involves chaining a response handler onto the `Request`. - -```swift -Alamofire.request("https://httpbin.org/get").responseJSON { response in - print(response.request) // original URL request - print(response.response) // HTTP URL response - print(response.data) // server data - print(response.result) // result of response serialization - - if let JSON = response.result.value { - print("JSON: \(JSON)") - } -} -``` - -In the above example, the `responseJSON` handler is appended to the `Request` to be executed once the `Request` is complete. Rather than blocking execution to wait for a response from the server, a [callback](http://en.wikipedia.org/wiki/Callback_%28computer_programming%29) in the form of a closure is specified to handle the response once it's received. The result of a request is only available inside the scope of a response closure. Any execution contingent on the response or data received from the server must be done within a response closure. - -> Networking in Alamofire is done _asynchronously_. Asynchronous programming may be a source of frustration to programmers unfamiliar with the concept, but there are [very good reasons](https://developer.apple.com/library/ios/qa/qa1693/_index.html) for doing it this way. - -Alamofire contains five different response handlers by default including: - -```swift -// Response Handler - Unserialized Response -func response( - queue: DispatchQueue?, - completionHandler: @escaping (DefaultDataResponse) -> Void) - -> Self - -// Response Data Handler - Serialized into Data -func responseData( - queue: DispatchQueue?, - completionHandler: @escaping (DataResponse) -> Void) - -> Self - -// Response String Handler - Serialized into String -func responseString( - queue: DispatchQueue?, - encoding: String.Encoding?, - completionHandler: @escaping (DataResponse) -> Void) - -> Self - -// Response JSON Handler - Serialized into Any -func responseJSON( - queue: DispatchQueue?, - completionHandler: @escaping (DataResponse) -> Void) - -> Self - -// Response PropertyList (plist) Handler - Serialized into Any -func responsePropertyList( - queue: DispatchQueue?, - completionHandler: @escaping (DataResponse) -> Void)) - -> Self -``` - -None of the response handlers perform any validation of the `HTTPURLResponse` it gets back from the server. - -> For example, response status codes in the `400..<499` and `500..<599` ranges do NOT automatically trigger an `Error`. Alamofire uses [Response Validation](#response-validation) method chaining to achieve this. - -#### Response Handler - -The `response` handler does NOT evaluate any of the response data. It merely forwards on all information directly from the URL session delegate. It is the Alamofire equivalent of using `cURL` to execute a `Request`. - -```swift -Alamofire.request("https://httpbin.org/get").response { response in - print("Request: \(response.request)") - print("Response: \(response.response)") - print("Error: \(response.error)") - - if let data = response.data, let utf8Text = String(data: data, encoding: .utf8) { - print("Data: \(utf8Text)") - } -} -``` - -> We strongly encourage you to leverage the other response serializers taking advantage of `Response` and `Result` types. - -#### Response Data Handler - -The `responseData` handler uses the `responseDataSerializer` (the object that serializes the server data into some other type) to extract the `Data` returned by the server. If no errors occur and `Data` is returned, the response `Result` will be a `.success` and the `value` will be of type `Data`. - -```swift -Alamofire.request("https://httpbin.org/get").responseData { response in - debugPrint("All Response Info: \(response)") - - if let data = response.result.value, let utf8Text = String(data: data, encoding: .utf8) { - print("Data: \(utf8Text)") - } -} -``` - -#### Response String Handler - -The `responseString` handler uses the `responseStringSerializer` to convert the `Data` returned by the server into a `String` with the specified encoding. If no errors occur and the server data is successfully serialized into a `String`, the response `Result` will be a `.success` and the `value` will be of type `String`. - -```swift -Alamofire.request("https://httpbin.org/get").responseString { response in - print("Success: \(response.result.isSuccess)") - print("Response String: \(response.result.value)") -} -``` - -> If no encoding is specified, Alamofire will use the text encoding specified in the `HTTPURLResponse` from the server. If the text encoding cannot be determined by the server response, it defaults to `.isoLatin1`. - -#### Response JSON Handler - -The `responseJSON` handler uses the `responseJSONSerializer` to convert the `Data` returned by the server into an `Any` type using the specified `JSONSerialization.ReadingOptions`. If no errors occur and the server data is successfully serialized into a JSON object, the response `Result` will be a `.success` and the `value` will be of type `Any`. - -```swift -Alamofire.request("https://httpbin.org/get").responseJSON { response in - debugPrint(response) - - if let json = response.result.value { - print("JSON: \(json)") - } -} -``` - -> All JSON serialization is handled by the `JSONSerialization` API in the `Foundation` framework. - -#### Chained Response Handlers - -Response handlers can even be chained: - -```swift -Alamofire.request("https://httpbin.org/get") - .responseString { response in - print("Response String: \(response.result.value)") - } - .responseJSON { response in - print("Response JSON: \(response.result.value)") - } -``` - -> It is important to note that using multiple response handlers on the same `Request` requires the server data to be serialized multiple times. Once for each response handler. - -#### Response Handler Queue - -Response handlers by default are executed on the main dispatch queue. However, a custom dispatch queue can be provided instead. - -```swift -let utilityQueue = DispatchQueue.global(qos: .utility) - -Alamofire.request("https://httpbin.org/get").responseJSON(queue: utilityQueue) { response in - print("Executing response handler on utility queue") -} -``` - -### Response Validation - -By default, Alamofire treats any completed request to be successful, regardless of the content of the response. Calling `validate` before a response handler causes an error to be generated if the response had an unacceptable status code or MIME type. - -#### Manual Validation - -```swift -Alamofire.request("https://httpbin.org/get") - .validate(statusCode: 200..<300) - .validate(contentType: ["application/json"]) - .responseData { response in - switch response.result { - case .success: - print("Validation Successful") - case .failure(let error): - print(error) - } - } -``` - -#### Automatic Validation - -Automatically validates status code within `200...299` range, and that the `Content-Type` header of the response matches the `Accept` header of the request, if one is provided. - -```swift -Alamofire.request("https://httpbin.org/get").validate().responseJSON { response in - switch response.result { - case .success: - print("Validation Successful") - case .failure(let error): - print(error) - } -} -``` - -### Response Caching - -Response Caching is handled on the system framework level by [`URLCache`](https://developer.apple.com/reference/foundation/urlcache). It provides a composite in-memory and on-disk cache and lets you manipulate the sizes of both the in-memory and on-disk portions. - -> By default, Alamofire leverages the shared `URLCache`. In order to customize it, see the [Session Manager Configurations](#session-manager) section. - -### HTTP Methods - -The `HTTPMethod` enumeration lists the HTTP methods defined in [RFC 7231 §4.3](http://tools.ietf.org/html/rfc7231#section-4.3): - -```swift -public enum HTTPMethod: String { - case options = "OPTIONS" - case get = "GET" - case head = "HEAD" - case post = "POST" - case put = "PUT" - case patch = "PATCH" - case delete = "DELETE" - case trace = "TRACE" - case connect = "CONNECT" -} -``` - -These values can be passed as the `method` argument to the `Alamofire.request` API: - -```swift -Alamofire.request("https://httpbin.org/get") // method defaults to `.get` - -Alamofire.request("https://httpbin.org/post", method: .post) -Alamofire.request("https://httpbin.org/put", method: .put) -Alamofire.request("https://httpbin.org/delete", method: .delete) -``` - -> The `Alamofire.request` method parameter defaults to `.get`. - -### Parameter Encoding - -Alamofire supports three types of parameter encoding including: `URL`, `JSON` and `PropertyList`. It can also support any custom encoding that conforms to the `ParameterEncoding` protocol. - -#### URL Encoding - -The `URLEncoding` type creates a url-encoded query string to be set as or appended to any existing URL query string or set as the HTTP body of the URL request. Whether the query string is set or appended to any existing URL query string or set as the HTTP body depends on the `Destination` of the encoding. The `Destination` enumeration has three cases: - -- `.methodDependent` - Applies encoded query string result to existing query string for `GET`, `HEAD` and `DELETE` requests and sets as the HTTP body for requests with any other HTTP method. -- `.queryString` - Sets or appends encoded query string result to existing query string. -- `.httpBody` - Sets encoded query string result as the HTTP body of the URL request. - -The `Content-Type` HTTP header field of an encoded request with HTTP body is set to `application/x-www-form-urlencoded; charset=utf-8`. Since there is no published specification for how to encode collection types, the convention of appending `[]` to the key for array values (`foo[]=1&foo[]=2`), and appending the key surrounded by square brackets for nested dictionary values (`foo[bar]=baz`). - -##### GET Request With URL-Encoded Parameters - -```swift -let parameters: Parameters = ["foo": "bar"] - -// All three of these calls are equivalent -Alamofire.request("https://httpbin.org/get", parameters: parameters) // encoding defaults to `URLEncoding.default` -Alamofire.request("https://httpbin.org/get", parameters: parameters, encoding: URLEncoding.default) -Alamofire.request("https://httpbin.org/get", parameters: parameters, encoding: URLEncoding(destination: .methodDependent)) - -// https://httpbin.org/get?foo=bar -``` - -##### POST Request With URL-Encoded Parameters - -```swift -let parameters: Parameters = [ - "foo": "bar", - "baz": ["a", 1], - "qux": [ - "x": 1, - "y": 2, - "z": 3 - ] -] - -// All three of these calls are equivalent -Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters) -Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: URLEncoding.default) -Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: URLEncoding.httpBody) - -// HTTP body: foo=bar&baz[]=a&baz[]=1&qux[x]=1&qux[y]=2&qux[z]=3 -``` - -#### JSON Encoding - -The `JSONEncoding` type creates a JSON representation of the parameters object, which is set as the HTTP body of the request. The `Content-Type` HTTP header field of an encoded request is set to `application/json`. - -##### POST Request with JSON-Encoded Parameters - -```swift -let parameters: Parameters = [ - "foo": [1,2,3], - "bar": [ - "baz": "qux" - ] -] - -// Both calls are equivalent -Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: JSONEncoding.default) -Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: JSONEncoding(options: [])) - -// HTTP body: {"foo": [1, 2, 3], "bar": {"baz": "qux"}} -``` - -#### Property List Encoding - -The `PropertyListEncoding` uses `PropertyListSerialization` to create a plist representation of the parameters object, according to the associated format and write options values, which is set as the body of the request. The `Content-Type` HTTP header field of an encoded request is set to `application/x-plist`. - -#### Custom Encoding - -In the event that the provided `ParameterEncoding` types do not meet your needs, you can create your own custom encoding. Here's a quick example of how you could build a custom `JSONStringArrayEncoding` type to encode a JSON string array onto a `Request`. - -```swift -struct JSONStringArrayEncoding: ParameterEncoding { - private let array: [String] - - init(array: [String]) { - self.array = array - } - - func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest { - var urlRequest = urlRequest.urlRequest - - let data = try JSONSerialization.data(withJSONObject: array, options: []) - - if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { - urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") - } - - urlRequest.httpBody = data - - return urlRequest - } -} -``` - -#### Manual Parameter Encoding of a URLRequest - -The `ParameterEncoding` APIs can be used outside of making network requests. - -```swift -let url = URL(string: "https://httpbin.org/get")! -var urlRequest = URLRequest(url: url) - -let parameters: Parameters = ["foo": "bar"] -let encodedURLRequest = try URLEncoding.queryString.encode(urlRequest, with: parameters) -``` - -### HTTP Headers - -Adding a custom HTTP header to a `Request` is supported directly in the global `request` method. This makes it easy to attach HTTP headers to a `Request` that can be constantly changing. - -```swift -let headers: HTTPHeaders = [ - "Authorization": "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==", - "Accept": "application/json" -] - -Alamofire.request("https://httpbin.org/headers", headers: headers).responseJSON { response in - debugPrint(response) -} -``` - -> For HTTP headers that do not change, it is recommended to set them on the `URLSessionConfiguration` so they are automatically applied to any `URLSessionTask` created by the underlying `URLSession`. For more information, see the [Session Manager Configurations](#session-manager) section. - -The default Alamofire `SessionManager` provides a default set of headers for every `Request`. These include: - -- `Accept-Encoding`, which defaults to `gzip;q=1.0, compress;q=0.5`, per [RFC 7230 §4.2.3](https://tools.ietf.org/html/rfc7230#section-4.2.3). -- `Accept-Language`, which defaults to up to the top 6 preferred languages on the system, formatted like `en;q=1.0`, per [RFC 7231 §5.3.5](https://tools.ietf.org/html/rfc7231#section-5.3.5). -- `User-Agent`, which contains versioning information about the current app. For example: `iOS Example/1.0 (com.alamofire.iOS-Example; build:1; iOS 10.0.0) Alamofire/4.0.0`, per [RFC 7231 §5.5.3](https://tools.ietf.org/html/rfc7231#section-5.5.3). - -If you need to customize these headers, a custom `URLSessionConfiguration` should be created, the `defaultHTTPHeaders` property updated and the configuration applied to a new `SessionManager` instance. - -### Authentication - -Authentication is handled on the system framework level by [`URLCredential`](https://developer.apple.com/reference/foundation/nsurlcredential) and [`URLAuthenticationChallenge`](https://developer.apple.com/reference/foundation/urlauthenticationchallenge). - -**Supported Authentication Schemes** - -- [HTTP Basic](http://en.wikipedia.org/wiki/Basic_access_authentication) -- [HTTP Digest](http://en.wikipedia.org/wiki/Digest_access_authentication) -- [Kerberos](http://en.wikipedia.org/wiki/Kerberos_%28protocol%29) -- [NTLM](http://en.wikipedia.org/wiki/NT_LAN_Manager) - -#### HTTP Basic Authentication - -The `authenticate` method on a `Request` will automatically provide a `URLCredential` to a `URLAuthenticationChallenge` when appropriate: - -```swift -let user = "user" -let password = "password" - -Alamofire.request("https://httpbin.org/basic-auth/\(user)/\(password)") - .authenticate(user: user, password: password) - .responseJSON { response in - debugPrint(response) - } -``` - -Depending upon your server implementation, an `Authorization` header may also be appropriate: - -```swift -let user = "user" -let password = "password" - -var headers: HTTPHeaders = [:] - -if let authorizationHeader = Request.authorizationHeader(user: user, password: password) { - headers[authorizationHeader.key] = authorizationHeader.value -} - -Alamofire.request("https://httpbin.org/basic-auth/user/password", headers: headers) - .responseJSON { response in - debugPrint(response) - } -``` - -#### Authentication with URLCredential - -```swift -let user = "user" -let password = "password" - -let credential = URLCredential(user: user, password: password, persistence: .forSession) - -Alamofire.request("https://httpbin.org/basic-auth/\(user)/\(password)") - .authenticate(usingCredential: credential) - .responseJSON { response in - debugPrint(response) - } -``` - -> It is important to note that when using a `URLCredential` for authentication, the underlying `URLSession` will actually end up making two requests if a challenge is issued by the server. The first request will not include the credential which "may" trigger a challenge from the server. The challenge is then received by Alamofire, the credential is appended and the request is retried by the underlying `URLSession`. - -### Downloading Data to a File - -Requests made in Alamofire that fetch data from a server can download the data in-memory or on-disk. The `Alamofire.request` APIs used in all the examples so far always downloads the server data in-memory. This is great for smaller payloads because it's more efficient, but really bad for larger payloads because the download could run your entire application out-of-memory. Because of this, you can also use the `Alamofire.download` APIs to download the server data to a temporary file on-disk. - -> This will only work on `macOS` as is. Other platforms don't allow access to the filesystem outside of your app's sandbox. To download files on other platforms, see the [Download File Destination](#download-file-destination) section. - -```swift -Alamofire.download("https://httpbin.org/image/png").responseData { response in - if let data = response.result.value { - let image = UIImage(data: data) - } -} -``` - -> The `Alamofire.download` APIs should also be used if you need to download data while your app is in the background. For more information, please see the [Session Manager Configurations](#session-manager) section. - -#### Download File Destination - -You can also provide a `DownloadFileDestination` closure to move the file from the temporary directory to a final destination. Before the temporary file is actually moved to the `destinationURL`, the `DownloadOptions` specified in the closure will be executed. The two currently supported `DownloadOptions` are: - -- `.createIntermediateDirectories` - Creates intermediate directories for the destination URL if specified. -- `.removePreviousFile` - Removes a previous file from the destination URL if specified. - -```swift -let destination: DownloadRequest.DownloadFileDestination = { _, _ in - let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] - let fileURL = documentsURL.appendPathComponent("pig.png") - - return (fileURL, [.removePreviousFile, .createIntermediateDirectories]) -} - -Alamofire.download(urlString, to: destination).response { response in - print(response) - - if response.error == nil, let imagePath = response.destinationURL?.path { - let image = UIImage(contentsOfFile: imagePath) - } -} -``` - -You can also use the suggested download destination API. - -```swift -let destination = DownloadRequest.suggestedDownloadDestination(directory: .documentDirectory) -Alamofire.download("https://httpbin.org/image/png", to: destination) -``` - -#### Download Progress - -Many times it can be helpful to report download progress to the user. Any `DownloadRequest` can report download progress using the `downloadProgress` API. - -```swift -Alamofire.download("https://httpbin.org/image/png") - .downloadProgress { progress in - print("Download Progress: \(progress.fractionCompleted)") - } - .responseData { response in - if let data = response.result.value { - let image = UIImage(data: data) - } - } -``` - -The `downloadProgress` API also takes a `queue` parameter which defines which `DispatchQueue` the download progress closure should be called on. - -```swift -let utilityQueue = DispatchQueue.global(qos: .utility) - -Alamofire.download("https://httpbin.org/image/png") - .downloadProgress(queue: utilityQueue) { progress in - print("Download Progress: \(progress.fractionCompleted)") - } - .responseData { response in - if let data = response.result.value { - let image = UIImage(data: data) - } - } -``` - -#### Resuming a Download - -If a `DownloadRequest` is cancelled or interrupted, the underlying URL session may generate resume data for the active `DownloadRequest`. If this happens, the resume data can be re-used to restart the `DownloadRequest` where it left off. The resume data can be accessed through the download response, then reused when trying to restart the request. - -> **IMPORTANT:** On the latest release of all the Apple platforms (iOS 10, macOS 10.12, tvOS 10, watchOS 3), `resumeData` is broken on background URL session configurations. There's an underlying bug in the `resumeData` generation logic where the data is written incorrectly and will always fail to resume the download. For more information about the bug and possible workarounds, please see this Stack Overflow [post](http://stackoverflow.com/a/39347461/1342462). - -```swift -class ImageRequestor { - private var resumeData: Data? - private var image: UIImage? - - func fetchImage(completion: (UIImage?) -> Void) { - guard image == nil else { completion(image) ; return } - - let destination: DownloadRequest.DownloadFileDestination = { _, _ in - let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] - let fileURL = documentsURL.appendPathComponent("pig.png") - - return (fileURL, [.removePreviousFile, .createIntermediateDirectories]) - } - - let request: DownloadRequest - - if let resumeData = resumeData { - request = Alamofire.download(resumingWith: resumeData) - } else { - request = Alamofire.download("https://httpbin.org/image/png") - } - - request.responseData { response in - switch response.result { - case .success(let data): - self.image = UIImage(data: data) - case .failure: - self.resumeData = response.resumeData - } - } - } -} -``` - -### Uploading Data to a Server - -When sending relatively small amounts of data to a server using JSON or URL encoded parameters, the `Alamofire.request` APIs are usually sufficient. If you need to send much larger amounts of data from a file URL or an `InputStream`, then the `Alamofire.upload` APIs are what you want to use. - -> The `Alamofire.upload` APIs should also be used if you need to upload data while your app is in the background. For more information, please see the [Session Manager Configurations](#session-manager) section. - -#### Uploading Data - -```swift -let imageData = UIPNGRepresentation(image)! - -Alamofire.upload(imageData, to: "https://httpbin.org/post").responseJSON { response in - debugPrint(response) -} -``` - -#### Uploading a File - -```swift -let fileURL = Bundle.main.url(forResource: "video", withExtension: "mov") - -Alamofire.upload(fileURL, to: "https://httpbin.org/post").responseJSON { response in - debugPrint(response) -} -``` - -#### Uploading Multipart Form Data - -```swift -Alamofire.upload( - multipartFormData: { multipartFormData in - multipartFormData.append(unicornImageURL, withName: "unicorn") - multipartFormData.append(rainbowImageURL, withName: "rainbow") - }, - to: "https://httpbin.org/post", - encodingCompletion: { encodingResult in - switch encodingResult { - case .success(let upload, _, _): - upload.responseJSON { response in - debugPrint(response) - } - case .failure(let encodingError): - print(encodingError) - } - } -) -``` - -#### Upload Progress - -While your user is waiting for their upload to complete, sometimes it can be handy to show the progress of the upload to the user. Any `UploadRequest` can report both upload progress and download progress of the response data using the `uploadProgress` and `downloadProgress` APIs. - -```swift -let fileURL = Bundle.main.url(forResource: "video", withExtension: "mov") - -Alamofire.upload(fileURL, to: "https://httpbin.org/post") - .uploadProgress { progress in // main queue by default - print("Upload Progress: \(progress.fractionCompleted)") - } - .downloadProgress { progress in // main queue by default - print("Download Progress: \(progress.fractionCompleted)") - } - .responseJSON { response in - debugPrint(response) - } -``` - -### Statistical Metrics - -#### Timeline - -Alamofire collects timings throughout the lifecycle of a `Request` and creates a `Timeline` object exposed as a property on all response types. - -```swift -Alamofire.request("https://httpbin.org/get").responseJSON { response in - print(response.timeline) -} -``` - -The above reports the following `Timeline` info: - -- `Latency`: 0.428 seconds -- `Request Duration`: 0.428 seconds -- `Serialization Duration`: 0.001 seconds -- `Total Duration`: 0.429 seconds - -#### URL Session Task Metrics - -In iOS and tvOS 10 and macOS 10.12, Apple introduced the new [URLSessionTaskMetrics](https://developer.apple.com/reference/foundation/urlsessiontaskmetrics) APIs. The task metrics encapsulate some fantastic statistical information about the request and response execution. The API is very similar to the `Timeline`, but provides many more statistics that Alamofire doesn't have access to compute. The metrics can be accessed through any response type. +## Open Radars -```swift -Alamofire.request("https://httpbin.org/get").responseJSON { response in - print(response.metrics) -} -``` +The following radars have some effect on the current implementation of Alamofire. -It's important to note that these APIs are only available on iOS and tvOS 10 and macOS 10.12. Therefore, depending on your deployment target, you may need to use these inside availability checks: +- [`rdar://21349340`](http://www.openradar.me/radar?id=5517037090635776) - Compiler throwing warning due to toll-free bridging issue in test case +- `rdar://26870455` - Background URL Session Configurations do not work in the simulator +- `rdar://26849668` - Some URLProtocol APIs do not properly handle `URLRequest` +- [`rdar://36082113`](http://openradar.appspot.com/radar?id=4942308441063424) - `URLSessionTaskMetrics` failing to link on watchOS 3.0+ -```swift -Alamofire.request("https://httpbin.org/get").responseJSON { response in - if #available(iOS 10.0. *) { - print(response.metrics) - } -} -``` +## Resolved Radars -### cURL Command Output +The following radars have been resolved over time after being filed against the Alamofire project. -Debugging platform issues can be frustrating. Thankfully, Alamofire `Request` objects conform to both the `CustomStringConvertible` and `CustomDebugStringConvertible` protocols to provide some VERY helpful debugging tools. +- [`rdar://26761490`](http://www.openradar.me/radar?id=5010235949318144) - Swift string interpolation causing memory leak with common usage (Resolved on 9/1/17 in Xcode 9 beta 6). -#### CustomStringConvertible +## FAQ -```swift -let request = Alamofire.request("https://httpbin.org/ip") +### What's the origin of the name Alamofire? -print(request) -// GET https://httpbin.org/ip (200) -``` +Alamofire is named after the [Alamo Fire flower](https://aggie-horticulture.tamu.edu/wildseed/alamofire.html), a hybrid variant of the Bluebonnet, the official state flower of Texas. -#### CustomDebugStringConvertible +### What logic belongs in a Router vs. a Request Adapter? -```swift -let request = Alamofire.request("https://httpbin.org/get", parameters: ["foo": "bar"]) -debugPrint(request) -``` - -Outputs: - -```bash -$ curl -i \ - -H "User-Agent: Alamofire/4.0.0" \ - -H "Accept-Encoding: gzip;q=1.0, compress;q=0.5" \ - -H "Accept-Language: en;q=1.0,fr;q=0.9,de;q=0.8,zh-Hans;q=0.7,zh-Hant;q=0.6,ja;q=0.5" \ - "https://httpbin.org/get?foo=bar" -``` - ---- - -## Advanced Usage - -Alamofire is built on `URLSession` and the Foundation URL Loading System. To make the most of this framework, it is recommended that you be familiar with the concepts and capabilities of the underlying networking stack. - -**Recommended Reading** - -- [URL Loading System Programming Guide](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html) -- [URLSession Class Reference](https://developer.apple.com/reference/foundation/nsurlsession) -- [URLCache Class Reference](https://developer.apple.com/reference/foundation/urlcache) -- [URLAuthenticationChallenge Class Reference](https://developer.apple.com/reference/foundation/urlauthenticationchallenge) - -### Session Manager - -Top-level convenience methods like `Alamofire.request` use a default instance of `Alamofire.SessionManager`, which is configured with the default `URLSessionConfiguration`. - -As such, the following two statements are equivalent: - -```swift -Alamofire.request("https://httpbin.org/get") -``` - -```swift -let sessionManager = Alamofire.SessionManager.default -sessionManager.request("https://httpbin.org/get") -``` - -Applications can create session managers for background and ephemeral sessions, as well as new managers that customize the default session configuration, such as for default headers (`httpAdditionalHeaders`) or timeout interval (`timeoutIntervalForRequest`). - -#### Creating a Session Manager with Default Configuration - -```swift -let configuration = URLSessionConfiguration.default -let sessionManager = Alamofire.SessionManager(configuration: configuration) -``` - -#### Creating a Session Manager with Background Configuration - -```swift -let configuration = URLSessionConfiguration.background(withIdentifier: "com.example.app.background") -let sessionManager = Alamofire.SessionManager(configuration: configuration) -``` - -#### Creating a Session Manager with Ephemeral Configuration - -```swift -let configuration = URLSessionConfiguration.ephemeral -let sessionManager = Alamofire.SessionManager(configuration: configuration) -``` - -#### Modifying the Session Configuration - -```swift -var defaultHeaders = Alamofire.SessionManager.defaultHTTPHeaders -defaultHeaders["DNT"] = "1 (Do Not Track Enabled)" - -let configuration = URLSessionConfiguration.default -configuration.httpAdditionalHeaders = defaultHeaders - -let sessionManager = Alamofire.SessionManager(configuration: configuration) -``` - -> This is **not** recommended for `Authorization` or `Content-Type` headers. Instead, use the `headers` parameter in the top-level `Alamofire.request` APIs, `URLRequestConvertible` and `ParameterEncoding`, respectively. - -### Session Delegate - -By default, an Alamofire `SessionManager` instance creates a `SessionDelegate` object to handle all the various types of delegate callbacks that are generated by the underlying `URLSession`. The implementations of each delegate method handle the most common use cases for these types of calls abstracting the complexity away from the top-level APIs. However, advanced users may find the need to override the default functionality for various reasons. - -#### Override Closures - -The first way to customize the `SessionDelegate` behavior is through the use of the override closures. Each closure gives you the ability to override the implementation of the matching `SessionDelegate` API, yet still use the default implementation for all other APIs. This makes it easy to customize subsets of the delegate functionality. Here are a few examples of some of the override closures available: - -```swift -/// Overrides default behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)`. -open var sessionDidReceiveChallenge: ((URLSession, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))? - -/// Overrides default behavior for URLSessionDelegate method `urlSessionDidFinishEvents(forBackgroundURLSession:)`. -open var sessionDidFinishEventsForBackgroundURLSession: ((URLSession) -> Void)? - -/// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)`. -open var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)? - -/// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:willCacheResponse:completionHandler:)`. -open var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)? -``` - -The following is a short example of how to use the `taskWillPerformHTTPRedirection` to avoid following redirects to any `apple.com` domains. - -```swift -let sessionManager = Alamofire.SessionManager(configuration: URLSessionConfiguration.default) -let delegate: Alamofire.SessionDelegate = sessionManager.delegate - -delegate.taskWillPerformHTTPRedirection = { session, task, response, request in - var finalRequest = request - - if - let originalRequest = task.originalRequest, - let urlString = originalRequest.url?.urlString, - urlString.contains("apple.com") - { - finalRequest = originalRequest - } - - return finalRequest -} -``` - -#### Subclassing - -Another way to override the default implementation of the `SessionDelegate` is to subclass it. Subclassing allows you completely customize the behavior of the API or to create a proxy for the API and still use the default implementation. Creating a proxy allows you to log events, emit notifications, provide pre and post hook implementations, etc. Here's a quick example of subclassing the `SessionDelegate` and logging a message when a redirect occurs. - -```swift -class LoggingSessionDelegate: SessionDelegate { - override func urlSession( - _ session: URLSession, - task: URLSessionTask, - willPerformHTTPRedirection response: HTTPURLResponse, - newRequest request: URLRequest, - completionHandler: @escaping (URLRequest?) -> Void) - { - print("URLSession will perform HTTP redirection to request: \(request)") - - super.urlSession( - session, - task: task, - willPerformHTTPRedirection: response, - newRequest: request, - completionHandler: completionHandler - ) - } -} -``` - -Generally speaking, either the default implementation or the override closures should provide the necessary functionality required. Subclassing should only be used as a last resort. - -> It is important to keep in mind that the `subdelegates` are initialized and destroyed in the default implementation. Be careful when subclassing to not introduce memory leaks. - -### Request - -The result of a `request`, `download`, `upload` or `stream` methods are a `DataRequest`, `DownloadRequest`, `UploadRequest` and `StreamRequest` which all inherit from `Request`. All `Request` instances are always created by an owning session manager, and never initialized directly. - -Each subclass has specialized methods such as `authenticate`, `validate`, `responseJSON` and `uploadProgress` that each return the caller instance in order to facilitate method chaining. - -Requests can be suspended, resumed and cancelled: - -- `suspend()`: Suspends the underlying task and dispatch queue. -- `resume()`: Resumes the underlying task and dispatch queue. If the owning manager does not have `startRequestsImmediately` set to `true`, the request must call `resume()` in order to start. -- `cancel()`: Cancels the underlying task, producing an error that is passed to any registered response handlers. - -### Routing Requests - -As apps grow in size, it's important to adopt common patterns as you build out your network stack. An important part of that design is how to route your requests. The Alamofire `URLConvertible` and `URLRequestConvertible` protocols along with the `Router` design pattern are here to help. - -#### URLConvertible - -Types adopting the `URLConvertible` protocol can be used to construct URLs, which are then used to construct URL requests internally. `String`, `URL`, and `URLComponents` conform to `URLConvertible` by default, allowing any of them to be passed as `url` parameters to the `request`, `upload`, and `download` methods: - -```swift -let urlString = "https://httpbin.org/post" -Alamofire.request(urlString, method: .post) - -let url = URL(string: urlString)! -Alamofire.request(url, method: .post) - -let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: true)! -Alamofire.request(urlComponents, method: .post) -``` - -Applications interacting with web applications in a significant manner are encouraged to have custom types conform to `URLConvertible` as a convenient way to map domain-specific models to server resources. - -##### Type-Safe Routing - -```swift -extension User: URLConvertible { - static let baseURLString = "https://example.com" - - func asURL() throws -> URL { - let urlString = User.baseURLString + "/users/\(username)/" - return try urlString.asURL() - } -} -``` - -```swift -let user = User(username: "mattt") -Alamofire.request(user) // https://example.com/users/mattt -``` - -#### URLRequestConvertible - -Types adopting the `URLRequestConvertible` protocol can be used to construct URL requests. `URLRequest` conforms to `URLRequestConvertible` by default, allowing it to be passed into `request`, `upload`, and `download` methods directly (this is the recommended way to specify custom HTTP body for individual requests): - -```swift -let url = URL(string: "https://httpbin.org/post")! -var urlRequest = URLRequest(url: url) -urlRequest.httpMethod = "POST" - -let parameters = ["foo": "bar"] - -do { - urlRequest.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: []) -} catch { - // No-op -} - -urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") - -Alamofire.request(urlRequest) -``` - -Applications interacting with web applications in a significant manner are encouraged to have custom types conform to `URLRequestConvertible` as a way to ensure consistency of requested endpoints. Such an approach can be used to abstract away server-side inconsistencies and provide type-safe routing, as well as manage authentication credentials and other state. - -##### API Parameter Abstraction - -```swift -enum Router: URLRequestConvertible { - case search(query: String, page: Int) - - static let baseURLString = "https://example.com" - static let perPage = 50 - - // MARK: URLRequestConvertible - - func asURLRequest() throws -> URLRequest { - let result: (path: String, parameters: Parameters) = { - switch self { - case let .search(query, page) where page > 0: - return ("/search", ["q": query, "offset": Router.perPage * page]) - case let .search(query, _): - return ("/search", ["q": query]) - } - }() - - let url = try Router.baseURLString.asURL() - let urlRequest = URLRequest(url: url.appendingPathComponent(result.path)) - - return try URLEncoding.default.encode(urlRequest, with: result.parameters) - } -} -``` - -```swift -Alamofire.request(Router.search(query: "foo bar", page: 1)) // https://example.com/search?q=foo%20bar&offset=50 -``` - -##### CRUD & Authorization - -```swift -import Alamofire - -enum Router: URLRequestConvertible { - case createUser(parameters: Parameters) - case readUser(username: String) - case updateUser(username: String, parameters: Parameters) - case destroyUser(username: String) - - static let baseURLString = "https://example.com" - - var method: HTTPMethod { - switch self { - case .createUser: - return .post - case .readUser: - return .get - case .updateUser: - return .put - case .destroyUser: - return .delete - } - } - - var path: String { - switch self { - case .createUser: - return "/users" - case .readUser(let username): - return "/users/\(username)" - case .updateUser(let username, _): - return "/users/\(username)" - case .destroyUser(let username): - return "/users/\(username)" - } - } - - // MARK: URLRequestConvertible - - func asURLRequest() throws -> URLRequest { - let url = try Router.baseURLString.asURL() - - var urlRequest = URLRequest(url: url.appendingPathComponent(path)) - urlRequest.httpMethod = method.rawValue - - switch self { - case .createUser(let parameters): - urlRequest = try URLEncoding.default.encode(urlRequest, with: parameters) - case .updateUser(_, let parameters): - urlRequest = try URLEncoding.default.encode(urlRequest, with: parameters) - default: - break - } - - return urlRequest - } -} -``` - -```swift -Alamofire.request(Router.readUser("mattt")) // GET https://example.com/users/mattt -``` - -### Adapting and Retrying Requests - -Most web services these days are behind some sort of authentication system. One of the more common ones today is OAuth. This generally involves generating an access token authorizing your application or user to call the various supported web services. While creating these initial access tokens can be laborsome, it can be even more complicated when your access token expires and you need to fetch a new one. There are many thread-safety issues that need to be considered. - -The `RequestAdapter` and `RequestRetrier` protocols were created to make it much easier to create a thread-safe authentication system for a specific set of web services. - -#### RequestAdapter - -The `RequestAdapter` protocol allows each `Request` made on a `SessionManager` to be inspected and adapted before being created. One very specific way to use an adapter is to append an `Authorization` header to requests behind a certain type of authentication. - -```swift -class AccessTokenAdapter: RequestAdapter { - private let accessToken: String - - init(accessToken: String) { - self.accessToken = accessToken - } - - func adapt(_ urlRequest: URLRequest) throws -> URLRequest { - var urlRequest = urlRequest - - if let urlString = urlRequest.url?.absoluteString, urlString.hasPrefix("https://httpbin.org") { - urlRequest.setValue("Bearer " + accessToken, forHTTPHeaderField: "Authorization") - } - - return urlRequest - } -} -``` - -```swift -let sessionManager = SessionManager() -sessionManager.adapter = AccessTokenAdapter(accessToken: "1234") - -sessionManager.request("https://httpbin.org/get") -``` - -#### RequestRetrier - -The `RequestRetrier` protocol allows a `Request` that encountered an `Error` while being executed to be retried. When using both the `RequestAdapter` and `RequestRetrier` protocols together, you can create credential refresh systems for OAuth1, OAuth2, Basic Auth and even exponential backoff retry policies. The possibilities are endless. Here's an example of how you could implement a refresh flow for OAuth2 access tokens. - -> **DISCLAIMER:** This is **NOT** a global `OAuth2` solution. It is merely an example demonstrating how one could use the `RequestAdapter` in conjunction with the `RequestRetrier` to create a thread-safe refresh system. - -> To reiterate, **do NOT copy** this sample code and drop it into a production application. This is merely an example. Each authentication system must be tailored to a particular platform and authentication type. - -```swift -class OAuth2Handler: RequestAdapter, RequestRetrier { - private typealias RefreshCompletion = (_ succeeded: Bool, _ accessToken: String?, _ refreshToken: String?) -> Void - - private let sessionManager: SessionManager = { - let configuration = URLSessionConfiguration.default - configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders - - return SessionManager(configuration: configuration) - }() - - private let lock = NSLock() - - private var clientID: String - private var baseURLString: String - private var accessToken: String - private var refreshToken: String - - private var isRefreshing = false - private var requestsToRetry: [RequestRetryCompletion] = [] - - // MARK: - Initialization - - public init(clientID: String, baseURLString: String, accessToken: String, refreshToken: String) { - self.clientID = clientID - self.baseURLString = baseURLString - self.accessToken = accessToken - self.refreshToken = refreshToken - } - - // MARK: - RequestAdapter - - func adapt(_ urlRequest: URLRequest) throws -> URLRequest { - if let urlString = urlRequest.url?.absoluteString, urlString.hasPrefix(baseURLString) { - var urlRequest = urlRequest - urlRequest.setValue("Bearer " + accessToken, forHTTPHeaderField: "Authorization") - return urlRequest - } - - return urlRequest - } - - // MARK: - RequestRetrier - - func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) { - lock.lock() ; defer { lock.unlock() } - - if let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401 { - requestsToRetry.append(completion) - - if !isRefreshing { - refreshTokens { [weak self] succeeded, accessToken, refreshToken in - guard let strongSelf = self else { return } - - strongSelf.lock.lock() ; defer { strongSelf.lock.unlock() } - - if let accessToken = accessToken, let refreshToken = refreshToken { - strongSelf.accessToken = accessToken - strongSelf.refreshToken = refreshToken - } - - strongSelf.requestsToRetry.forEach { $0(succeeded, 0.0) } - strongSelf.requestsToRetry.removeAll() - } - } - } else { - completion(false, 0.0) - } - } - - // MARK: - Private - Refresh Tokens - - private func refreshTokens(completion: @escaping RefreshCompletion) { - guard !isRefreshing else { return } - - isRefreshing = true - - let urlString = "\(baseURLString)/oauth2/token" - - let parameters: [String: Any] = [ - "access_token": accessToken, - "refresh_token": refreshToken, - "client_id": clientID, - "grant_type": "refresh_token" - ] - - sessionManager.request(urlString, method: .post, parameters: parameters, encoding: JSONEncoding.default) - .responseJSON { [weak self] response in - guard let strongSelf = self else { return } - - if - let json = response.result.value as? [String: Any], - let accessToken = json["access_token"] as? String, - let refreshToken = json["refresh_token"] as? String - { - completion(true, accessToken, refreshToken) - } else { - completion(false, nil, nil) - } - - strongSelf.isRefreshing = false - } - } -} -``` - -```swift -let baseURLString = "https://some.domain-behind-oauth2.com" - -let oauthHandler = OAuth2Handler( - clientID: "12345678", - baseURLString: baseURLString, - accessToken: "abcd1234", - refreshToken: "ef56789a" -) - -let sessionManager = SessionManager() -sessionManager.adapter = oauthHandler -sessionManager.retrier = oauthHandler - -let urlString = "\(baseURLString)/some/endpoint" - -sessionManager.request(urlString).validate().responseJSON { response in - debugPrint(response) -} -``` - -Once the `OAuth2Handler` is applied as both the `adapter` and `retrier` for the `SessionManager`, it will handle an invalid access token error by automatically refreshing the access token and retrying all failed requests in the same order they failed. - -> If you needed them to execute in the same order they were created, you could sort them by their task identifiers. - -The example above only checks for a `401` response code which is not nearly robust enough, but does demonstrate how one could check for an invalid access token error. In a production application, one would want to check the `realm` and most likely the `www-authenticate` header response although it depends on the OAuth2 implementation. - -Another important note is that this authentication system could be shared between multiple session managers. For example, you may need to use both a `default` and `ephemeral` session configuration for the same set of web services. The example above allows the same `oauthHandler` instance to be shared across multiple session managers to manage the single refresh flow. - -### Custom Response Serialization - -Alamofire provides built-in response serialization for data, strings, JSON, and property lists: - -```swift -Alamofire.request(...).responseData { (resp: DataResponse) in ... } -Alamofire.request(...).responseString { (resp: DataResponse) in ... } -Alamofire.request(...).responseJSON { (resp: DataResponse) in ... } -Alamofire.request(...).responsePropertyList { resp: DataResponse) in ... } -``` - -Those responses wrap deserialized *values* (Data, String, Any) or *errors* (network, validation errors), as well as *meta-data* (URL request, HTTP headers, status code, [metrics](#statistical-metrics), ...). - -You have several ways to customize all of those response elements: - -- [Response Mapping](#response-mapping) -- [Handling Errors](#handling-errors) -- [Creating a Custom Response Serializer](#creating-a-custom-response-serializer) -- [Generic Response Object Serialization](#generic-response-object-serialization) - -#### Response Mapping - -Response mapping is the simplest way to produce customized responses. It transforms the value of a response, while preserving eventual errors and meta-data. For example, you can turn a json response `DataResponse` into a response that holds an application model, such as `DataResponse`. You perform response mapping with the `DataResponse.map` method: - -```swift -Alamofire.request("https://example.com/users/mattt").responseJSON { (response: DataResponse) in - let userResponse = response.map { json in - // We assume an existing User(json: Any) initializer - return User(json: json) - } - - // Process userResponse, of type DataResponse: - if let user = userResponse.value { - print("User: { username: \(user.username), name: \(user.name) }") - } -} -``` - -When the transformation may throw an error, use `flatMap` instead: - -```swift -Alamofire.request("https://example.com/users/mattt").responseJSON { response in - let userResponse = response.flatMap { json in - try User(json: json) - } -} -``` - -Response mapping is a good fit for your custom completion handlers: - -```swift -@discardableResult -func loadUser(completionHandler: @escaping (DataResponse) -> Void) -> Alamofire.DataRequest { - return Alamofire.request("https://example.com/users/mattt").responseJSON { response in - let userResponse = response.flatMap { json in - try User(json: json) - } - - completionHandler(userResponse) - } -} - -loadUser { response in - if let user = userResponse.value { - print("User: { username: \(user.username), name: \(user.name) }") - } -} -``` - -When the map/flatMap closure may process a big amount of data, make sure you execute it outside of the main thread: - -```swift -@discardableResult -func loadUser(completionHandler: @escaping (DataResponse) -> Void) -> Alamofire.DataRequest { - let utilityQueue = DispatchQueue.global(qos: .utility) - - return Alamofire.request("https://example.com/users/mattt").responseJSON(queue: utilityQueue) { response in - let userResponse = response.flatMap { json in - try User(json: json) - } - - DispatchQueue.main.async { - completionHandler(userResponse) - } - } -} -``` - -`map` and `flatMap` are also available for [download responses](#downloading-data-to-a-file). - -#### Handling Errors - -Before implementing custom response serializers or object serialization methods, it's important to consider how to handle any errors that may occur. There are two basic options: passing existing errors along unmodified, to be dealt with at response time; or, wrapping all errors in an `Error` type specific to your app. - -For example, here's a simple `BackendError` enum which will be used in later examples: - -```swift -enum BackendError: Error { - case network(error: Error) // Capture any underlying Error from the URLSession API - case dataSerialization(error: Error) - case jsonSerialization(error: Error) - case xmlSerialization(error: Error) - case objectSerialization(reason: String) -} -``` - -#### Creating a Custom Response Serializer - -Alamofire provides built-in response serialization for strings, JSON, and property lists, but others can be added in extensions on `Alamofire.DataRequest` and / or `Alamofire.DownloadRequest`. - -For example, here's how a response handler using [Ono](https://github.com/mattt/Ono) might be implemented: - -```swift -extension DataRequest { - static func xmlResponseSerializer() -> DataResponseSerializer { - return DataResponseSerializer { request, response, data, error in - // Pass through any underlying URLSession error to the .network case. - guard error == nil else { return .failure(BackendError.network(error: error!)) } - - // Use Alamofire's existing data serializer to extract the data, passing the error as nil, as it has - // already been handled. - let result = Request.serializeResponseData(response: response, data: data, error: nil) - - guard case let .success(validData) = result else { - return .failure(BackendError.dataSerialization(error: result.error! as! AFError)) - } - - do { - let xml = try ONOXMLDocument(data: validData) - return .success(xml) - } catch { - return .failure(BackendError.xmlSerialization(error: error)) - } - } - } - - @discardableResult - func responseXMLDocument( - queue: DispatchQueue? = nil, - completionHandler: @escaping (DataResponse) -> Void) - -> Self - { - return response( - queue: queue, - responseSerializer: DataRequest.xmlResponseSerializer(), - completionHandler: completionHandler - ) - } -} -``` - -#### Generic Response Object Serialization - -Generics can be used to provide automatic, type-safe response object serialization. - -```swift -protocol ResponseObjectSerializable { - init?(response: HTTPURLResponse, representation: Any) -} - -extension DataRequest { - func responseObject( - queue: DispatchQueue? = nil, - completionHandler: @escaping (DataResponse) -> Void) - -> Self - { - let responseSerializer = DataResponseSerializer { request, response, data, error in - guard error == nil else { return .failure(BackendError.network(error: error!)) } - - let jsonResponseSerializer = DataRequest.jsonResponseSerializer(options: .allowFragments) - let result = jsonResponseSerializer.serializeResponse(request, response, data, nil) - - guard case let .success(jsonObject) = result else { - return .failure(BackendError.jsonSerialization(error: result.error!)) - } - - guard let response = response, let responseObject = T(response: response, representation: jsonObject) else { - return .failure(BackendError.objectSerialization(reason: "JSON could not be serialized: \(jsonObject)")) - } - - return .success(responseObject) - } - - return response(queue: queue, responseSerializer: responseSerializer, completionHandler: completionHandler) - } -} -``` - -```swift -struct User: ResponseObjectSerializable, CustomStringConvertible { - let username: String - let name: String - - var description: String { - return "User: { username: \(username), name: \(name) }" - } - - init?(response: HTTPURLResponse, representation: Any) { - guard - let username = response.url?.lastPathComponent, - let representation = representation as? [String: Any], - let name = representation["name"] as? String - else { return nil } - - self.username = username - self.name = name - } -} -``` - -```swift -Alamofire.request("https://example.com/users/mattt").responseObject { (response: DataResponse) in - debugPrint(response) - - if let user = response.result.value { - print("User: { username: \(user.username), name: \(user.name) }") - } -} -``` - -The same approach can also be used to handle endpoints that return a representation of a collection of objects: - -```swift -protocol ResponseCollectionSerializable { - static func collection(from response: HTTPURLResponse, withRepresentation representation: Any) -> [Self] -} - -extension ResponseCollectionSerializable where Self: ResponseObjectSerializable { - static func collection(from response: HTTPURLResponse, withRepresentation representation: Any) -> [Self] { - var collection: [Self] = [] - - if let representation = representation as? [[String: Any]] { - for itemRepresentation in representation { - if let item = Self(response: response, representation: itemRepresentation) { - collection.append(item) - } - } - } - - return collection - } -} -``` - -```swift -extension DataRequest { - @discardableResult - func responseCollection( - queue: DispatchQueue? = nil, - completionHandler: @escaping (DataResponse<[T]>) -> Void) -> Self - { - let responseSerializer = DataResponseSerializer<[T]> { request, response, data, error in - guard error == nil else { return .failure(BackendError.network(error: error!)) } - - let jsonSerializer = DataRequest.jsonResponseSerializer(options: .allowFragments) - let result = jsonSerializer.serializeResponse(request, response, data, nil) - - guard case let .success(jsonObject) = result else { - return .failure(BackendError.jsonSerialization(error: result.error!)) - } - - guard let response = response else { - let reason = "Response collection could not be serialized due to nil response." - return .failure(BackendError.objectSerialization(reason: reason)) - } - - return .success(T.collection(from: response, withRepresentation: jsonObject)) - } - - return response(responseSerializer: responseSerializer, completionHandler: completionHandler) - } -} -``` - -```swift -struct User: ResponseObjectSerializable, ResponseCollectionSerializable, CustomStringConvertible { - let username: String - let name: String - - var description: String { - return "User: { username: \(username), name: \(name) }" - } - - init?(response: HTTPURLResponse, representation: Any) { - guard - let username = response.url?.lastPathComponent, - let representation = representation as? [String: Any], - let name = representation["name"] as? String - else { return nil } - - self.username = username - self.name = name - } -} -``` - -```swift -Alamofire.request("https://example.com/users").responseCollection { (response: DataResponse<[User]>) in - debugPrint(response) - - if let users = response.result.value { - users.forEach { print("- \($0)") } - } -} -``` - -### Security - -Using a secure HTTPS connection when communicating with servers and web services is an important step in securing sensitive data. By default, Alamofire will evaluate the certificate chain provided by the server using Apple's built in validation provided by the Security framework. While this guarantees the certificate chain is valid, it does not prevent man-in-the-middle (MITM) attacks or other potential vulnerabilities. In order to mitigate MITM attacks, applications dealing with sensitive customer data or financial information should use certificate or public key pinning provided by the `ServerTrustPolicy`. - -#### ServerTrustPolicy - -The `ServerTrustPolicy` enumeration evaluates the server trust generally provided by an `URLAuthenticationChallenge` when connecting to a server over a secure HTTPS connection. - -```swift -let serverTrustPolicy = ServerTrustPolicy.pinCertificates( - certificates: ServerTrustPolicy.certificates(), - validateCertificateChain: true, - validateHost: true -) -``` - -There are many different cases of server trust evaluation giving you complete control over the validation process: - -* `performDefaultEvaluation`: Uses the default server trust evaluation while allowing you to control whether to validate the host provided by the challenge. -* `pinCertificates`: Uses the pinned certificates to validate the server trust. The server trust is considered valid if one of the pinned certificates match one of the server certificates. -* `pinPublicKeys`: Uses the pinned public keys to validate the server trust. The server trust is considered valid if one of the pinned public keys match one of the server certificate public keys. -* `disableEvaluation`: Disables all evaluation which in turn will always consider any server trust as valid. -* `customEvaluation`: Uses the associated closure to evaluate the validity of the server trust thus giving you complete control over the validation process. Use with caution. - -#### Server Trust Policy Manager - -The `ServerTrustPolicyManager` is responsible for storing an internal mapping of server trust policies to a particular host. This allows Alamofire to evaluate each host against a different server trust policy. - -```swift -let serverTrustPolicies: [String: ServerTrustPolicy] = [ - "test.example.com": .pinCertificates( - certificates: ServerTrustPolicy.certificates(), - validateCertificateChain: true, - validateHost: true - ), - "insecure.expired-apis.com": .disableEvaluation -] - -let sessionManager = SessionManager( - serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies) -) -``` - -> Make sure to keep a reference to the new `SessionManager` instance, otherwise your requests will all get cancelled when your `sessionManager` is deallocated. - -These server trust policies will result in the following behavior: - -- `test.example.com` will always use certificate pinning with certificate chain and host validation enabled thus requiring the following criteria to be met to allow the TLS handshake to succeed: - - Certificate chain MUST be valid. - - Certificate chain MUST include one of the pinned certificates. - - Challenge host MUST match the host in the certificate chain's leaf certificate. -- `insecure.expired-apis.com` will never evaluate the certificate chain and will always allow the TLS handshake to succeed. -- All other hosts will use the default evaluation provided by Apple. - -##### Subclassing Server Trust Policy Manager - -If you find yourself needing more flexible server trust policy matching behavior (i.e. wildcarded domains), then subclass the `ServerTrustPolicyManager` and override the `serverTrustPolicyForHost` method with your own custom implementation. - -```swift -class CustomServerTrustPolicyManager: ServerTrustPolicyManager { - override func serverTrustPolicy(forHost host: String) -> ServerTrustPolicy? { - var policy: ServerTrustPolicy? - - // Implement your custom domain matching behavior... - - return policy - } -} -``` - -#### Validating the Host - -The `.performDefaultEvaluation`, `.pinCertificates` and `.pinPublicKeys` server trust policies all take a `validateHost` parameter. Setting the value to `true` will cause the server trust evaluation to verify that hostname in the certificate matches the hostname of the challenge. If they do not match, evaluation will fail. A `validateHost` value of `false` will still evaluate the full certificate chain, but will not validate the hostname of the leaf certificate. - -> It is recommended that `validateHost` always be set to `true` in production environments. - -#### Validating the Certificate Chain - -Pinning certificates and public keys both have the option of validating the certificate chain using the `validateCertificateChain` parameter. By setting this value to `true`, the full certificate chain will be evaluated in addition to performing a byte equality check against the pinned certificates or public keys. A value of `false` will skip the certificate chain validation, but will still perform the byte equality check. - -There are several cases where it may make sense to disable certificate chain validation. The most common use cases for disabling validation are self-signed and expired certificates. The evaluation would always fail in both of these cases, but the byte equality check will still ensure you are receiving the certificate you expect from the server. - -> It is recommended that `validateCertificateChain` always be set to `true` in production environments. - -#### App Transport Security - -With the addition of App Transport Security (ATS) in iOS 9, it is possible that using a custom `ServerTrustPolicyManager` with several `ServerTrustPolicy` objects will have no effect. If you continuously see `CFNetwork SSLHandshake failed (-9806)` errors, you have probably run into this problem. Apple's ATS system overrides the entire challenge system unless you configure the ATS settings in your app's plist to disable enough of it to allow your app to evaluate the server trust. - -If you run into this problem (high probability with self-signed certificates), you can work around this issue by adding the following to your `Info.plist`. - -```xml - - NSAppTransportSecurity - - NSExceptionDomains - - example.com - - NSExceptionAllowsInsecureHTTPLoads - - NSExceptionRequiresForwardSecrecy - - NSIncludesSubdomains - - - NSTemporaryExceptionMinimumTLSVersion - TLSv1.2 - - - - -``` - -Whether you need to set the `NSExceptionRequiresForwardSecrecy` to `NO` depends on whether your TLS connection is using an allowed cipher suite. In certain cases, it will need to be set to `NO`. The `NSExceptionAllowsInsecureHTTPLoads` MUST be set to `YES` in order to allow the `SessionDelegate` to receive challenge callbacks. Once the challenge callbacks are being called, the `ServerTrustPolicyManager` will take over the server trust evaluation. You may also need to specify the `NSTemporaryExceptionMinimumTLSVersion` if you're trying to connect to a host that only supports TLS versions less than `1.2`. - -> It is recommended to always use valid certificates in production environments. - -### Network Reachability - -The `NetworkReachabilityManager` listens for reachability changes of hosts and addresses for both WWAN and WiFi network interfaces. - -```swift -let manager = NetworkReachabilityManager(host: "www.apple.com") - -manager?.listener = { status in - print("Network Status Changed: \(status)") -} - -manager?.startListening() -``` - -> Make sure to remember to retain the `manager` in the above example, or no status changes will be reported. -> Also, do not include the scheme in the `host` string or reachability won't function correctly. - -There are some important things to remember when using network reachability to determine what to do next. - -- **Do NOT** use Reachability to determine if a network request should be sent. - - You should **ALWAYS** send it. -- When Reachability is restored, use the event to retry failed network requests. - - Even though the network requests may still fail, this is a good moment to retry them. -- The network reachability status can be useful for determining why a network request may have failed. - - If a network request fails, it is more useful to tell the user that the network request failed due to being offline rather than a more technical error, such as "request timed out." - -> It is recommended to check out [WWDC 2012 Session 706, "Networking Best Practices"](https://developer.apple.com/videos/play/wwdc2012-706/) for more info. - ---- - -## Open Radars - -The following radars have some effect on the current implementation of Alamofire. - -- [`rdar://21349340`](http://www.openradar.me/radar?id=5517037090635776) - Compiler throwing warning due to toll-free bridging issue in test case -- [`rdar://26761490`](http://www.openradar.me/radar?id=5010235949318144) - Swift string interpolation causing memory leak with common usage -- `rdar://26870455` - Background URL Session Configurations do not work in the simulator -- `rdar://26849668` - Some URLProtocol APIs do not properly handle `URLRequest` - -## FAQ - -### What's the origin of the name Alamofire? - -Alamofire is named after the [Alamo Fire flower](https://aggie-horticulture.tamu.edu/wildseed/alamofire.html), a hybrid variant of the Bluebonnet, the official state flower of Texas. - -### What logic belongs in a Router vs. a Request Adapter? - -Simple, static data such as paths, parameters and common headers belong in the `Router`. Dynamic data such as an `Authorization` header whose value can changed based on an authentication system belongs in a `RequestAdapter`. +Simple, static data such as paths, parameters and common headers belong in the `Router`. Dynamic data such as an `Authorization` header whose value can changed based on an authentication system belongs in a `RequestAdapter`. The reason the dynamic data MUST be placed into the `RequestAdapter` is to support retry operations. When a `Request` is retried, the original request is not rebuilt meaning the `Router` will not be called again. The `RequestAdapter` is called again allowing the dynamic data to be updated on the original request before retrying the `Request`. ---- - ## Credits Alamofire is owned and maintained by the [Alamofire Software Foundation](http://alamofire.org). You can follow them on Twitter at [@AlamofireSF](https://twitter.com/AlamofireSF) for project updates and releases. @@ -1837,18 +220,23 @@ If you believe you have identified a security vulnerability with Alamofire, you ## Donations -The [ASF](https://github.com/Alamofire/Foundation#members) is looking to raise money to officially register as a federal non-profit organization. Registering will allow us members to gain some legal protections and also allow us to put donations to use, tax free. Donating to the ASF will enable us to: +The [ASF](https://github.com/Alamofire/Foundation#members) is looking to raise money to officially stay registered as a federal non-profit organization. +Registering will allow us members to gain some legal protections and also allow us to put donations to use, tax free. +Donating to the ASF will enable us to: -- Pay our legal fees to register as a federal non-profit organization - Pay our yearly legal fees to keep the non-profit in good status - Pay for our mail servers to help us stay on top of all questions and security issues - Potentially fund test servers to make it easier for us to test the edge cases - Potentially fund developers to work on one of our projects full-time -The community adoption of the ASF libraries has been amazing. We are greatly humbled by your enthusiasm around the projects, and want to continue to do everything we can to move the needle forward. With your continued support, the ASF will be able to improve its reach and also provide better legal safety for the core members. If you use any of our libraries for work, see if your employers would be interested in donating. Our initial goal is to raise $1000 to get all our legal ducks in a row and kickstart this campaign. Any amount you can donate today to help us reach our goal would be greatly appreciated. +The community adoption of the ASF libraries has been amazing. +We are greatly humbled by your enthusiasm around the projects, and want to continue to do everything we can to move the needle forward. +With your continued support, the ASF will be able to improve its reach and also provide better legal safety for the core members. +If you use any of our libraries for work, see if your employers would be interested in donating. +Any amount you can donate today to help us reach our goal would be greatly appreciated. -Click here to lend your support to: Alamofire Software Foundation and make a donation at pledgie.com ! +[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=W34WPEE74APJQ) ## License -Alamofire is released under the MIT license. See LICENSE for details. +Alamofire is released under the MIT license. [See LICENSE](https://github.com/Alamofire/Alamofire/blob/master/LICENSE) for details. diff --git a/Pods/Alamofire/Source/AFError.swift b/Pods/Alamofire/Source/AFError.swift index f047695..8b90d84 100644 --- a/Pods/Alamofire/Source/AFError.swift +++ b/Pods/Alamofire/Source/AFError.swift @@ -1,7 +1,7 @@ // // AFError.swift // -// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Pods/Alamofire/Source/Alamofire.swift b/Pods/Alamofire/Source/Alamofire.swift index 86d54d8..2fcc05c 100644 --- a/Pods/Alamofire/Source/Alamofire.swift +++ b/Pods/Alamofire/Source/Alamofire.swift @@ -1,7 +1,7 @@ // // Alamofire.swift // -// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -53,7 +53,7 @@ extension URL: URLConvertible { } extension URLComponents: URLConvertible { - /// Returns a URL if `url` is not nil, otherise throws an `Error`. + /// Returns a URL if `url` is not nil, otherwise throws an `Error`. /// /// - throws: An `AFError.invalidURL` if `url` is `nil`. /// diff --git a/Pods/Alamofire/Source/DispatchQueue+Alamofire.swift b/Pods/Alamofire/Source/DispatchQueue+Alamofire.swift index 78e214e..dea3ebc 100644 --- a/Pods/Alamofire/Source/DispatchQueue+Alamofire.swift +++ b/Pods/Alamofire/Source/DispatchQueue+Alamofire.swift @@ -1,7 +1,7 @@ // // DispatchQueue+Alamofire.swift // -// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Pods/Alamofire/Source/MultipartFormData.swift b/Pods/Alamofire/Source/MultipartFormData.swift index 6d0d556..057e68b 100644 --- a/Pods/Alamofire/Source/MultipartFormData.swift +++ b/Pods/Alamofire/Source/MultipartFormData.swift @@ -1,7 +1,7 @@ // // MultipartFormData.swift // -// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -92,7 +92,7 @@ open class MultipartFormData { // MARK: - Properties /// The `Content-Type` header value containing the boundary used to generate the `multipart/form-data`. - open var contentType: String { return "multipart/form-data; boundary=\(boundary)" } + open lazy var contentType: String = "multipart/form-data; boundary=\(self.boundary)" /// The content length of all body parts used to generate the `multipart/form-data` not including the boundaries. public var contentLength: UInt64 { return bodyParts.reduce(0) { $0 + $1.bodyContentLength } } diff --git a/Pods/Alamofire/Source/NetworkReachabilityManager.swift b/Pods/Alamofire/Source/NetworkReachabilityManager.swift index 888818d..3ff2e7f 100644 --- a/Pods/Alamofire/Source/NetworkReachabilityManager.swift +++ b/Pods/Alamofire/Source/NetworkReachabilityManager.swift @@ -1,7 +1,7 @@ // // NetworkReachabilityManager.swift // -// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -33,7 +33,7 @@ import SystemConfiguration /// Reachability can be used to determine background information about why a network operation failed, or to retry /// network requests when a connection is established. It should not be used to prevent a user from initiating a network /// request, as it's possible that an initial request may be required to establish reachability. -public class NetworkReachabilityManager { +open class NetworkReachabilityManager { /// Defines the various states of network reachability. /// /// - unknown: It is unknown whether the network is reachable. @@ -61,27 +61,27 @@ public class NetworkReachabilityManager { // MARK: - Properties /// Whether the network is currently reachable. - public var isReachable: Bool { return isReachableOnWWAN || isReachableOnEthernetOrWiFi } + open var isReachable: Bool { return isReachableOnWWAN || isReachableOnEthernetOrWiFi } /// Whether the network is currently reachable over the WWAN interface. - public var isReachableOnWWAN: Bool { return networkReachabilityStatus == .reachable(.wwan) } + open var isReachableOnWWAN: Bool { return networkReachabilityStatus == .reachable(.wwan) } /// Whether the network is currently reachable over Ethernet or WiFi interface. - public var isReachableOnEthernetOrWiFi: Bool { return networkReachabilityStatus == .reachable(.ethernetOrWiFi) } + open var isReachableOnEthernetOrWiFi: Bool { return networkReachabilityStatus == .reachable(.ethernetOrWiFi) } /// The current network reachability status. - public var networkReachabilityStatus: NetworkReachabilityStatus { + open var networkReachabilityStatus: NetworkReachabilityStatus { guard let flags = self.flags else { return .unknown } return networkReachabilityStatusForFlags(flags) } /// The dispatch queue to execute the `listener` closure on. - public var listenerQueue: DispatchQueue = DispatchQueue.main + open var listenerQueue: DispatchQueue = DispatchQueue.main /// A closure executed when the network reachability status changes. - public var listener: Listener? + open var listener: Listener? - private var flags: SCNetworkReachabilityFlags? { + open var flags: SCNetworkReachabilityFlags? { var flags = SCNetworkReachabilityFlags() if SCNetworkReachabilityGetFlags(reachability, &flags) { @@ -92,7 +92,7 @@ public class NetworkReachabilityManager { } private let reachability: SCNetworkReachability - private var previousFlags: SCNetworkReachabilityFlags + open var previousFlags: SCNetworkReachabilityFlags // MARK: - Initialization @@ -141,7 +141,7 @@ public class NetworkReachabilityManager { /// /// - returns: `true` if listening was started successfully, `false` otherwise. @discardableResult - public func startListening() -> Bool { + open func startListening() -> Bool { var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil) context.info = Unmanaged.passUnretained(self).toOpaque() @@ -165,7 +165,7 @@ public class NetworkReachabilityManager { } /// Stops listening for changes in network reachability status. - public func stopListening() { + open func stopListening() { SCNetworkReachabilitySetCallback(reachability, nil, nil) SCNetworkReachabilitySetDispatchQueue(reachability, nil) } @@ -182,21 +182,24 @@ public class NetworkReachabilityManager { // MARK: - Internal - Network Reachability Status func networkReachabilityStatusForFlags(_ flags: SCNetworkReachabilityFlags) -> NetworkReachabilityStatus { - guard flags.contains(.reachable) else { return .notReachable } + guard isNetworkReachable(with: flags) else { return .notReachable } - var networkStatus: NetworkReachabilityStatus = .notReachable + var networkStatus: NetworkReachabilityStatus = .reachable(.ethernetOrWiFi) - if !flags.contains(.connectionRequired) { networkStatus = .reachable(.ethernetOrWiFi) } + #if os(iOS) + if flags.contains(.isWWAN) { networkStatus = .reachable(.wwan) } + #endif - if flags.contains(.connectionOnDemand) || flags.contains(.connectionOnTraffic) { - if !flags.contains(.interventionRequired) { networkStatus = .reachable(.ethernetOrWiFi) } - } + return networkStatus + } - #if os(iOS) - if flags.contains(.isWWAN) { networkStatus = .reachable(.wwan) } - #endif + func isNetworkReachable(with flags: SCNetworkReachabilityFlags) -> Bool { + let isReachable = flags.contains(.reachable) + let needsConnection = flags.contains(.connectionRequired) + let canConnectAutomatically = flags.contains(.connectionOnDemand) || flags.contains(.connectionOnTraffic) + let canConnectWithoutUserInteraction = canConnectAutomatically && !flags.contains(.interventionRequired) - return networkStatus + return isReachable && (!needsConnection || canConnectWithoutUserInteraction) } } diff --git a/Pods/Alamofire/Source/Notifications.swift b/Pods/Alamofire/Source/Notifications.swift index 81f6e37..e1b6120 100644 --- a/Pods/Alamofire/Source/Notifications.swift +++ b/Pods/Alamofire/Source/Notifications.swift @@ -1,7 +1,7 @@ // // Notifications.swift // -// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -48,5 +48,8 @@ extension Notification { public struct Key { /// User info dictionary key representing the `URLSessionTask` associated with the notification. public static let Task = "org.alamofire.notification.key.task" + + /// User info dictionary key representing the responseData associated with the notification. + public static let ResponseData = "org.alamofire.notification.key.responseData" } } diff --git a/Pods/Alamofire/Source/ParameterEncoding.swift b/Pods/Alamofire/Source/ParameterEncoding.swift index 242f6a8..4a54f2d 100644 --- a/Pods/Alamofire/Source/ParameterEncoding.swift +++ b/Pods/Alamofire/Source/ParameterEncoding.swift @@ -1,7 +1,7 @@ // // ParameterEncoding.swift // -// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -64,9 +64,15 @@ public protocol ParameterEncoding { /// the HTTP body depends on the destination of the encoding. /// /// The `Content-Type` HTTP header field of an encoded request with HTTP body is set to -/// `application/x-www-form-urlencoded; charset=utf-8`. Since there is no published specification for how to encode -/// collection types, the convention of appending `[]` to the key for array values (`foo[]=1&foo[]=2`), and appending -/// the key surrounded by square brackets for nested dictionary values (`foo[bar]=baz`). +/// `application/x-www-form-urlencoded; charset=utf-8`. +/// +/// There is no published specification for how to encode collection types. By default the convention of appending +/// `[]` to the key for array values (`foo[]=1&foo[]=2`), and appending the key surrounded by square brackets for +/// nested dictionary values (`foo[bar]=baz`) is used. Optionally, `ArrayEncoding` can be used to omit the +/// square brackets appended to array keys. +/// +/// `BoolEncoding` can be used to configure how boolean values are encoded. The default behavior is to encode +/// `true` as 1 and `false` as 0. public struct URLEncoding: ParameterEncoding { // MARK: Helper Types @@ -82,6 +88,41 @@ public struct URLEncoding: ParameterEncoding { case methodDependent, queryString, httpBody } + /// Configures how `Array` parameters are encoded. + /// + /// - brackets: An empty set of square brackets is appended to the key for every value. + /// This is the default behavior. + /// - noBrackets: No brackets are appended. The key is encoded as is. + public enum ArrayEncoding { + case brackets, noBrackets + + func encode(key: String) -> String { + switch self { + case .brackets: + return "\(key)[]" + case .noBrackets: + return key + } + } + } + + /// Configures how `Bool` parameters are encoded. + /// + /// - numeric: Encode `true` as `1` and `false` as `0`. This is the default behavior. + /// - literal: Encode `true` and `false` as string literals. + public enum BoolEncoding { + case numeric, literal + + func encode(value: Bool) -> String { + switch self { + case .numeric: + return value ? "1" : "0" + case .literal: + return value ? "true" : "false" + } + } + } + // MARK: Properties /// Returns a default `URLEncoding` instance. @@ -99,15 +140,25 @@ public struct URLEncoding: ParameterEncoding { /// The destination defining where the encoded query string is to be applied to the URL request. public let destination: Destination + /// The encoding to use for `Array` parameters. + public let arrayEncoding: ArrayEncoding + + /// The encoding to use for `Bool` parameters. + public let boolEncoding: BoolEncoding + // MARK: Initialization /// Creates a `URLEncoding` instance using the specified destination. /// /// - parameter destination: The destination defining where the encoded query string is to be applied. + /// - parameter arrayEncoding: The encoding to use for `Array` parameters. + /// - parameter boolEncoding: The encoding to use for `Bool` parameters. /// /// - returns: The new `URLEncoding` instance. - public init(destination: Destination = .methodDependent) { + public init(destination: Destination = .methodDependent, arrayEncoding: ArrayEncoding = .brackets, boolEncoding: BoolEncoding = .numeric) { self.destination = destination + self.arrayEncoding = arrayEncoding + self.boolEncoding = boolEncoding } // MARK: Encoding @@ -161,16 +212,16 @@ public struct URLEncoding: ParameterEncoding { } } else if let array = value as? [Any] { for value in array { - components += queryComponents(fromKey: "\(key)[]", value: value) + components += queryComponents(fromKey: arrayEncoding.encode(key: key), value: value) } } else if let value = value as? NSNumber { if value.isBool { - components.append((escape(key), escape((value.boolValue ? "1" : "0")))) + components.append((escape(key), escape(boolEncoding.encode(value: value.boolValue)))) } else { components.append((escape(key), escape("\(value)"))) } } else if let bool = value as? Bool { - components.append((escape(key), escape((bool ? "1" : "0")))) + components.append((escape(key), escape(boolEncoding.encode(value: bool)))) } else { components.append((escape(key), escape("\(value)"))) } @@ -223,9 +274,9 @@ public struct URLEncoding: ParameterEncoding { let endIndex = string.index(index, offsetBy: batchSize, limitedBy: string.endIndex) ?? string.endIndex let range = startIndex.. (key: String, value: String)? { + open class func authorizationHeader(user: String, password: String) -> (key: String, value: String)? { guard let data = "\(user):\(password)".data(using: .utf8) else { return nil } let credential = data.base64EncodedString(options: []) @@ -269,7 +269,7 @@ extension Request: CustomDebugStringConvertible { } func cURLRepresentation() -> String { - var components = ["$ curl -i"] + var components = ["$ curl -v"] guard let request = self.request, let url = request.url, @@ -293,11 +293,12 @@ extension Request: CustomDebugStringConvertible { if let credentials = credentialStorage.credentials(for: protectionSpace)?.values { for credential in credentials { - components.append("-u \(credential.user!):\(credential.password!)") + guard let user = credential.user, let password = credential.password else { continue } + components.append("-u \(user):\(password)") } } else { - if let credential = delegate.credential { - components.append("-u \(credential.user!):\(credential.password!)") + if let credential = delegate.credential, let user = credential.user, let password = credential.password { + components.append("-u \(user):\(password)") } } } @@ -308,7 +309,12 @@ extension Request: CustomDebugStringConvertible { let cookies = cookieStorage.cookies(for: url), !cookies.isEmpty { let string = cookies.reduce("") { $0 + "\($1.name)=\($1.value);" } + + #if swift(>=3.2) + components.append("-b \"\(string[.. = ... + /// let withMyError = possibleData.mapError { MyError.error($0) } + /// + /// - Parameter transform: A closure that takes the error of the instance. + /// - Returns: A `DataResponse` instance containing the result of the transform. + public func mapError(_ transform: (Error) -> E) -> DataResponse { + var response = DataResponse( + request: request, + response: self.response, + data: data, + result: result.mapError(transform), + timeline: timeline + ) + + response._metrics = _metrics + + return response + } + + /// Evaluates the specified closure when the `DataResponse` is a failure, passing the unwrapped error as a parameter. + /// + /// Use the `flatMapError` function with a closure that may throw an error. For example: + /// + /// let possibleData: DataResponse = ... + /// let possibleObject = possibleData.flatMapError { + /// try someFailableFunction(taking: $0) + /// } + /// + /// - Parameter transform: A throwing closure that takes the error of the instance. + /// + /// - Returns: A `DataResponse` instance containing the result of the transform. + public func flatMapError(_ transform: (Error) throws -> E) -> DataResponse { + var response = DataResponse( + request: request, + response: self.response, + data: data, + result: result.flatMapError(transform), + timeline: timeline + ) + + response._metrics = _metrics + + return response + } } // MARK: - @@ -409,6 +458,59 @@ extension DownloadResponse { return response } + + /// Evaluates the specified closure when the `DownloadResponse` is a failure, passing the unwrapped error as a parameter. + /// + /// Use the `mapError` function with a closure that does not throw. For example: + /// + /// let possibleData: DownloadResponse = ... + /// let withMyError = possibleData.mapError { MyError.error($0) } + /// + /// - Parameter transform: A closure that takes the error of the instance. + /// - Returns: A `DownloadResponse` instance containing the result of the transform. + public func mapError(_ transform: (Error) -> E) -> DownloadResponse { + var response = DownloadResponse( + request: request, + response: self.response, + temporaryURL: temporaryURL, + destinationURL: destinationURL, + resumeData: resumeData, + result: result.mapError(transform), + timeline: timeline + ) + + response._metrics = _metrics + + return response + } + + /// Evaluates the specified closure when the `DownloadResponse` is a failure, passing the unwrapped error as a parameter. + /// + /// Use the `flatMapError` function with a closure that may throw an error. For example: + /// + /// let possibleData: DownloadResponse = ... + /// let possibleObject = possibleData.flatMapError { + /// try someFailableFunction(taking: $0) + /// } + /// + /// - Parameter transform: A throwing closure that takes the error of the instance. + /// + /// - Returns: A `DownloadResponse` instance containing the result of the transform. + public func flatMapError(_ transform: (Error) throws -> E) -> DownloadResponse { + var response = DownloadResponse( + request: request, + response: self.response, + temporaryURL: temporaryURL, + destinationURL: destinationURL, + resumeData: resumeData, + result: result.flatMapError(transform), + timeline: timeline + ) + + response._metrics = _metrics + + return response + } } // MARK: - diff --git a/Pods/Alamofire/Source/ResponseSerialization.swift b/Pods/Alamofire/Source/ResponseSerialization.swift index 47780fd..3333726 100644 --- a/Pods/Alamofire/Source/ResponseSerialization.swift +++ b/Pods/Alamofire/Source/ResponseSerialization.swift @@ -1,7 +1,7 @@ // // ResponseSerialization.swift // -// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -88,11 +88,12 @@ public struct DownloadResponseSerializer: DownloadResponseSerializerProto extension Request { var timeline: Timeline { + let requestStartTime = self.startTime ?? CFAbsoluteTimeGetCurrent() let requestCompletedTime = self.endTime ?? CFAbsoluteTimeGetCurrent() let initialResponseTime = self.delegate.initialResponseTime ?? requestCompletedTime return Timeline( - requestStartTime: self.startTime ?? CFAbsoluteTimeGetCurrent(), + requestStartTime: requestStartTime, initialResponseTime: initialResponseTime, requestCompletedTime: requestCompletedTime, serializationCompletedTime: CFAbsoluteTimeGetCurrent() @@ -367,13 +368,13 @@ extension Request { var convertedEncoding = encoding - if let encodingName = response?.textEncodingName as CFString!, convertedEncoding == nil { + if let encodingName = response?.textEncodingName as CFString?, convertedEncoding == nil { convertedEncoding = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding( CFStringConvertIANACharSetNameToEncoding(encodingName)) ) } - let actualEncoding = convertedEncoding ?? String.Encoding.isoLatin1 + let actualEncoding = convertedEncoding ?? .isoLatin1 if let string = String(data: validData, encoding: actualEncoding) { return .success(string) diff --git a/Pods/Alamofire/Source/Result.swift b/Pods/Alamofire/Source/Result.swift index c13b1fc..df62e12 100644 --- a/Pods/Alamofire/Source/Result.swift +++ b/Pods/Alamofire/Source/Result.swift @@ -1,7 +1,7 @@ // // Result.swift // -// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -162,7 +162,7 @@ extension Result { /// try print(noInt.unwrap()) /// // Throws error /// - /// - parameter transform: A closure that takes the success value of the result instance. + /// - parameter transform: A closure that takes the success value of the `Result` instance. /// /// - returns: A `Result` containing the result of the given closure. If this instance is a failure, returns the /// same failure. @@ -200,4 +200,101 @@ extension Result { return .failure(error) } } + + /// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter. + /// + /// Use the `mapError` function with a closure that does not throw. For example: + /// + /// let possibleData: Result = .failure(someError) + /// let withMyError: Result = possibleData.mapError { MyError.error($0) } + /// + /// - Parameter transform: A closure that takes the error of the instance. + /// - Returns: A `Result` instance containing the result of the transform. If this instance is a success, returns + /// the same instance. + public func mapError(_ transform: (Error) -> T) -> Result { + switch self { + case .failure(let error): + return .failure(transform(error)) + case .success: + return self + } + } + + /// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter. + /// + /// Use the `flatMapError` function with a closure that may throw an error. For example: + /// + /// let possibleData: Result = .success(Data(...)) + /// let possibleObject = possibleData.flatMapError { + /// try someFailableFunction(taking: $0) + /// } + /// + /// - Parameter transform: A throwing closure that takes the error of the instance. + /// + /// - Returns: A `Result` instance containing the result of the transform. If this instance is a success, returns + /// the same instance. + public func flatMapError(_ transform: (Error) throws -> T) -> Result { + switch self { + case .failure(let error): + do { + return try .failure(transform(error)) + } catch { + return .failure(error) + } + case .success: + return self + } + } + + /// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter. + /// + /// Use the `withValue` function to evaluate the passed closure without modifying the `Result` instance. + /// + /// - Parameter closure: A closure that takes the success value of this instance. + /// - Returns: This `Result` instance, unmodified. + @discardableResult + public func withValue(_ closure: (Value) -> Void) -> Result { + if case let .success(value) = self { closure(value) } + + return self + } + + /// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter. + /// + /// Use the `withError` function to evaluate the passed closure without modifying the `Result` instance. + /// + /// - Parameter closure: A closure that takes the success value of this instance. + /// - Returns: This `Result` instance, unmodified. + @discardableResult + public func withError(_ closure: (Error) -> Void) -> Result { + if case let .failure(error) = self { closure(error) } + + return self + } + + /// Evaluates the specified closure when the `Result` is a success. + /// + /// Use the `ifSuccess` function to evaluate the passed closure without modifying the `Result` instance. + /// + /// - Parameter closure: A `Void` closure. + /// - Returns: This `Result` instance, unmodified. + @discardableResult + public func ifSuccess(_ closure: () -> Void) -> Result { + if isSuccess { closure() } + + return self + } + + /// Evaluates the specified closure when the `Result` is a failure. + /// + /// Use the `ifFailure` function to evaluate the passed closure without modifying the `Result` instance. + /// + /// - Parameter closure: A `Void` closure. + /// - Returns: This `Result` instance, unmodified. + @discardableResult + public func ifFailure(_ closure: () -> Void) -> Result { + if isFailure { closure() } + + return self + } } diff --git a/Pods/Alamofire/Source/ServerTrustPolicy.swift b/Pods/Alamofire/Source/ServerTrustPolicy.swift index 9c0e7c8..7f44c8d 100644 --- a/Pods/Alamofire/Source/ServerTrustPolicy.swift +++ b/Pods/Alamofire/Source/ServerTrustPolicy.swift @@ -1,7 +1,7 @@ // // ServerTrustPolicy.swift // -// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -27,7 +27,7 @@ import Foundation /// Responsible for managing the mapping of `ServerTrustPolicy` objects to a given host. open class ServerTrustPolicyManager { /// The dictionary of policies mapped to a particular host. - open let policies: [String: ServerTrustPolicy] + public let policies: [String: ServerTrustPolicy] /// Initializes the `ServerTrustPolicyManager` instance with the given policies. /// diff --git a/Pods/Alamofire/Source/SessionDelegate.swift b/Pods/Alamofire/Source/SessionDelegate.swift index 27ad881..03bcb7c 100644 --- a/Pods/Alamofire/Source/SessionDelegate.swift +++ b/Pods/Alamofire/Source/SessionDelegate.swift @@ -1,7 +1,7 @@ // // SessionDelegate.swift // -// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -36,7 +36,7 @@ open class SessionDelegate: NSObject { open var sessionDidReceiveChallenge: ((URLSession, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))? /// Overrides all behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)` and requires the caller to call the `completionHandler`. - open var sessionDidReceiveChallengeWithCompletion: ((URLSession, URLAuthenticationChallenge, (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)? + open var sessionDidReceiveChallengeWithCompletion: ((URLSession, URLAuthenticationChallenge, @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)? /// Overrides default behavior for URLSessionDelegate method `urlSessionDidFinishEvents(forBackgroundURLSession:)`. open var sessionDidFinishEventsForBackgroundURLSession: ((URLSession) -> Void)? @@ -48,21 +48,21 @@ open class SessionDelegate: NSObject { /// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)` and /// requires the caller to call the `completionHandler`. - open var taskWillPerformHTTPRedirectionWithCompletion: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest, (URLRequest?) -> Void) -> Void)? + open var taskWillPerformHTTPRedirectionWithCompletion: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest, @escaping (URLRequest?) -> Void) -> Void)? /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didReceive:completionHandler:)`. open var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))? /// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:didReceive:completionHandler:)` and /// requires the caller to call the `completionHandler`. - open var taskDidReceiveChallengeWithCompletion: ((URLSession, URLSessionTask, URLAuthenticationChallenge, (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)? + open var taskDidReceiveChallengeWithCompletion: ((URLSession, URLSessionTask, URLAuthenticationChallenge, @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)? /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:needNewBodyStream:)`. open var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> InputStream?)? /// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:needNewBodyStream:)` and /// requires the caller to call the `completionHandler`. - open var taskNeedNewBodyStreamWithCompletion: ((URLSession, URLSessionTask, (InputStream?) -> Void) -> Void)? + open var taskNeedNewBodyStreamWithCompletion: ((URLSession, URLSessionTask, @escaping (InputStream?) -> Void) -> Void)? /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)`. open var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)? @@ -77,7 +77,7 @@ open class SessionDelegate: NSObject { /// Overrides all behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:completionHandler:)` and /// requires caller to call the `completionHandler`. - open var dataTaskDidReceiveResponseWithCompletion: ((URLSession, URLSessionDataTask, URLResponse, (URLSession.ResponseDisposition) -> Void) -> Void)? + open var dataTaskDidReceiveResponseWithCompletion: ((URLSession, URLSessionDataTask, URLResponse, @escaping (URLSession.ResponseDisposition) -> Void) -> Void)? /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didBecome:)`. open var dataTaskDidBecomeDownloadTask: ((URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)? @@ -90,7 +90,7 @@ open class SessionDelegate: NSObject { /// Overrides all behavior for URLSessionDataDelegate method `urlSession(_:dataTask:willCacheResponse:completionHandler:)` and /// requires caller to call the `completionHandler`. - open var dataTaskWillCacheResponseWithCompletion: ((URLSession, URLSessionDataTask, CachedURLResponse, (CachedURLResponse?) -> Void) -> Void)? + open var dataTaskWillCacheResponseWithCompletion: ((URLSession, URLSessionDataTask, CachedURLResponse, @escaping (CachedURLResponse?) -> Void) -> Void)? // MARK: URLSessionDownloadDelegate Overrides @@ -163,7 +163,7 @@ open class SessionDelegate: NSObject { var retrier: RequestRetrier? weak var sessionManager: SessionManager? - private var requests: [Int: Request] = [:] + var requests: [Int: Request] = [:] private let lock = NSLock() /// Access the task delegate for the specified task in a thread-safe manner. @@ -442,10 +442,16 @@ extension SessionDelegate: URLSessionTaskDelegate { strongSelf[task]?.delegate.urlSession(session, task: task, didCompleteWithError: error) + var userInfo: [String: Any] = [Notification.Key.Task: task] + + if let data = (strongSelf[task]?.delegate as? DataTaskDelegate)?.data { + userInfo[Notification.Key.ResponseData] = data + } + NotificationCenter.default.post( name: Notification.Name.Task.DidComplete, object: strongSelf, - userInfo: [Notification.Key.Task: task] + userInfo: userInfo ) strongSelf[task] = nil @@ -462,8 +468,8 @@ extension SessionDelegate: URLSessionTaskDelegate { // Determine whether an error has occurred var error: Error? = error - if let taskDelegate = self[task]?.delegate, taskDelegate.error != nil { - error = taskDelegate.error + if request.delegate.error != nil { + error = request.delegate.error } /// If an error occurred and the retrier is set, asynchronously ask the retrier if the request diff --git a/Pods/Alamofire/Source/SessionManager.swift b/Pods/Alamofire/Source/SessionManager.swift index 450f750..8779efd 100644 --- a/Pods/Alamofire/Source/SessionManager.swift +++ b/Pods/Alamofire/Source/SessionManager.swift @@ -1,7 +1,7 @@ // // SessionManager.swift // -// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -45,7 +45,7 @@ open class SessionManager { /// A default instance of `SessionManager`, used by top-level Alamofire request methods, and suitable for use /// directly for any ad hoc requests. - open static let `default`: SessionManager = { + public static let `default`: SessionManager = { let configuration = URLSessionConfiguration.default configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders @@ -53,7 +53,7 @@ open class SessionManager { }() /// Creates default values for the "Accept-Encoding", "Accept-Language" and "User-Agent" headers. - open static let defaultHTTPHeaders: HTTPHeaders = { + public static let defaultHTTPHeaders: HTTPHeaders = { // Accept-Encoding HTTP Header; see https://tools.ietf.org/html/rfc7230#section-4.2.3 let acceptEncoding: String = "gzip;q=1.0, compress;q=0.5" @@ -118,13 +118,13 @@ open class SessionManager { }() /// Default memory threshold used when encoding `MultipartFormData` in bytes. - open static let multipartFormDataEncodingMemoryThreshold: UInt64 = 10_000_000 + public static let multipartFormDataEncodingMemoryThreshold: UInt64 = 10_000_000 /// The underlying session. - open let session: URLSession + public let session: URLSession /// The session delegate handling all the task and session delegate callbacks. - open let delegate: SessionDelegate + public let delegate: SessionDelegate /// Whether to start requests immediately after being constructed. `true` by default. open var startRequestsImmediately: Bool = true @@ -249,6 +249,7 @@ open class SessionManager { /// - parameter urlRequest: The URL request. /// /// - returns: The created `DataRequest`. + @discardableResult open func request(_ urlRequest: URLRequestConvertible) -> DataRequest { var originalRequest: URLRequest? @@ -847,6 +848,10 @@ open class SessionManager { do { let task = try originalTask.task(session: session, adapter: adapter, queue: queue) + if let originalTask = request.task { + delegate[originalTask] = nil // removes the old request to avoid endless growth + } + request.delegate.task = task // resets all task delegate data request.retryCount += 1 diff --git a/Pods/Alamofire/Source/TaskDelegate.swift b/Pods/Alamofire/Source/TaskDelegate.swift index d4fd216..8e19888 100644 --- a/Pods/Alamofire/Source/TaskDelegate.swift +++ b/Pods/Alamofire/Source/TaskDelegate.swift @@ -1,7 +1,7 @@ // // TaskDelegate.swift // -// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -31,7 +31,7 @@ open class TaskDelegate: NSObject { // MARK: Properties /// The serial operation queue used to execute all operations after the task completes. - open let queue: OperationQueue + public let queue: OperationQueue /// The data returned by the server. public var data: Data? { return nil } @@ -40,17 +40,30 @@ open class TaskDelegate: NSObject { public var error: Error? var task: URLSessionTask? { - didSet { reset() } + set { + taskLock.lock(); defer { taskLock.unlock() } + _task = newValue + } + get { + taskLock.lock(); defer { taskLock.unlock() } + return _task + } } var initialResponseTime: CFAbsoluteTime? var credential: URLCredential? var metrics: AnyObject? // URLSessionTaskMetrics + private var _task: URLSessionTask? { + didSet { reset() } + } + + private let taskLock = NSLock() + // MARK: Lifecycle init(task: URLSessionTask?) { - self.task = task + _task = task self.queue = { let operationQueue = OperationQueue() diff --git a/Pods/Alamofire/Source/Timeline.swift b/Pods/Alamofire/Source/Timeline.swift index 1440989..181c988 100644 --- a/Pods/Alamofire/Source/Timeline.swift +++ b/Pods/Alamofire/Source/Timeline.swift @@ -1,7 +1,7 @@ // // Timeline.swift // -// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/Pods/Alamofire/Source/Validation.swift b/Pods/Alamofire/Source/Validation.swift index c405d02..ec2c5c3 100644 --- a/Pods/Alamofire/Source/Validation.swift +++ b/Pods/Alamofire/Source/Validation.swift @@ -1,7 +1,7 @@ // // Validation.swift // -// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) +// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -48,7 +48,13 @@ extension Request { init?(_ string: String) { let components: [String] = { let stripped = string.trimmingCharacters(in: .whitespacesAndNewlines) + + #if swift(>=3.2) + let split = stripped[..<(stripped.range(of: ";")?.lowerBound ?? stripped.endIndex)] + #else let split = stripped.substring(to: stripped.range(of: ";")?.lowerBound ?? stripped.endIndex) + #endif + return split.components(separatedBy: "/") }() diff --git a/Pods/Eureka/README.md b/Pods/Eureka/README.md index 19bb0c4..7b47856 100644 --- a/Pods/Eureka/README.md +++ b/Pods/Eureka/README.md @@ -3,7 +3,7 @@

Build status Platform iOS -Swift 3 compatible +Swift 4 compatible Carthage compatible CocoaPods compatible License: MIT @@ -12,11 +12,23 @@ Made with ❤️ by [XMARTLABS](http://xmartlabs.com). This is the re-creation of [XLForm] in Swift. +[简体中文](Documentation/README_CN.md) + ## Overview - - - + + + + + + +
+ + + + + +
## Contents @@ -29,7 +41,9 @@ Made with ❤️ by [XMARTLABS](http://xmartlabs.com). This is the re-creation o + [Section Header and Footer] + [Dynamically hide and show rows (or sections)] + [List sections] + + [Multivalued sections] + [Validations] + + [Swipe Actions] * [Custom rows] + [Basic custom rows] + [Custom inline rows] @@ -42,16 +56,23 @@ Made with ❤️ by [XMARTLABS](http://xmartlabs.com). This is the re-creation o ## Requirements -* iOS 8.0+ -* Xcode 8+ -* Swift 3 +* Xcode 9.2+ +* Swift 4+ ### Example project You can clone and run the Example project to see examples of most of Eureka's features. - - + + + + + +
+ + + +
## Usage @@ -86,7 +107,7 @@ class MyFormViewController: FormViewController { In the example we create two sections with standard rows, the result is this:

-Screenshot of Custom Cells +Screenshot of Custom Cells
You could create a form by just setting up the `form` property by yourself without extending from `FormViewController` but this method is typically more convenient. @@ -305,7 +326,7 @@ Section(){ section in In this case we are hiding and showing whole sections. -To accomplish this each row has an `hidden` variable of optional type `Condition` which can be set using a function or `NSPredicate`. +To accomplish this each row has a `hidden` variable of optional type `Condition` which can be set using a function or `NSPredicate`. #### Hiding using a function condition @@ -379,7 +400,8 @@ Note that if you want to disable a row permanently you can also set `disabled` v ### List Sections To display a list of options, Eureka includes a special section called `SelectableSection`. -When creating one you need to pass the type of row to use in the options and the `selectionStyle`. The `selectionStyle` is an enum which can be either `multipleSelection` or `singleSelection(enableDeselection: Bool)` where the `enableDeselection` parameter determines if the selected rows can be deselected or not. +When creating one you need to pass the type of row to use in the options and the `selectionType`. +The `selectionType` is an enum which can be either `multipleSelection` or `singleSelection(enableDeselection: Bool)` where the `enableDeselection` parameter determines if the selected rows can be deselected or not. ```swift form +++ SelectableSection>("Where do you live", selectionType: .singleSelection(enableDeselection: true)) @@ -421,10 +443,58 @@ Additionally you can setup list of options to be grouped by sections using follo - `sectionFooterTitleForKey` - a closure that returns footer title for a section for particular key. +### Multivalued Sections + +Eureka supports multiple values for a certain field (such as telephone numbers in a contact) by using Multivalued sections. It allows us to easily create insertable, deletable and reorderable sections. + +Screenshot of Multivalued Section + +#### How to create a multivalued section + +In order to create a multivalued section we have to use `MultivaluedSection` type instead of the regular `Section` type. `MultivaluedSection` extends `Section` and has some additional properties to configure multivalued section behavior. + +let's dive into a code example... + +```swift +form +++ + MultivaluedSection(multivaluedOptions: [.Reorder, .Insert, .Delete], + header: "Multivalued TextField", + footer: ".Insert adds a 'Add Item' (Add New Tag) button row as last cell.") { + $0.addButtonProvider = { section in + return ButtonRow(){ + $0.title = "Add New Tag" + } + } + $0.multivaluedRowToInsertAt = { index in + return NameRow() { + $0.placeholder = "Tag Name" + } + } + $0 <<< NameRow() { + $0.placeholder = "Tag Name" + } + } +``` + +Previous code snippet shows how to create a multivalued section. In this case we want to insert, delete and reorder rows as multivaluedOptions argument indicates. + +`addButtonProvider` allows us to customize the button row which inserts a new row when tapped and `multivaluedOptions` contains `.Insert` value. + +`multivaluedRowToInsertAt` closure property is called by Eureka each time a new row needs to be inserted. In order to provide the row to add into multivalued section we should set this property. Eureka passes the index as closure parameter. Notice that we can return any kind of row, even custom rows, even though in most cases multivalued section rows are of the same type. + +Eureka automatically adds a button row when we create a insertable multivalued section. We can customize how the this button row looks like as we explained before. `showInsertIconInAddButton` property indicates if plus button (insert style) should appear in the left of the button, true by default. + +There are some considerations we need to have in mind when creating insertable sections. Any row added to the insertable multivalued section should be placed above the row that Eureka automatically adds to insert new rows. This can be easily achieved by adding these additional rows to the section from inside the section's initializer closure (last parameter of section initializer) so then Eureka adds the adds insert button at the end of the section. + +#### Editing mode + +By default Eureka will set the tableView's `isEditing` to true only if there is a MultivaluedSection in the form. This will be done in `viewWillAppear` the first time a form is presented. + +For more information on how to use multivalued sections please take a look at Eureka example project which contains several usage examples. ### Validations -Eureka 2.0.0 introduces the very requested build-in validations feature. +Eureka 2.0.0 introduces the much requested built-in validations feature. A row has a collection of `Rules` and a specific configuration that determines when validation rules should be evaluated. @@ -515,6 +585,53 @@ If you want to validate the entire form (all the rows) you can manually invoke F Each row has the `validationErrors` property that can be used to retrieve all validation errors. This property just holds the validation error list of the latest row validation execution, which means it doesn't evaluate the validation rules of the row. +#### Note on types + +As expected, the Rules must use the same types as the Row object. Be extra careful to check the row type used. You might see a compiler error ("Incorrect arugment label in call (have 'rule:' expected 'ruleSet:')" that is not pointing to the problem when mixing types. + +### Swipe Actions + +Eureka 4.1.0 introduces the swipe feature. + +You are now able to define multiple `leadingSwipe` and `trailingSwipe` actions per row. As swipe actions depend on iOS system features, `leadingSwipe` is available on iOS 11.0+ only. + +Let's see how to define swipe actions. + +```swift +let row = TextRow() { + let deleteAction = SwipeAction( + style: .destructive, + title: "Delete", + handler: { (action, row, completionHandler) in + //add your code here. + //make sure you call the completionHandler once done. + completionHandler?(true) + }) + deleteAction.image = UIImage(named: "icon-trash") + + $0.trailingSwipe.actions = [deleteAction] + $0.trailingSwipe.performsFirstActionWithFullSwipe = true + + //please be aware: `leadingSwipe` is only available on iOS 11+ only + let infoAction = SwipeAction( + style: .normal, + title: "Info", + handler: { (action, row, completionHandler) in + //add your code here. + //make sure you call the completionHandler once done. + completionHandler?(true) + }) + infoAction.backgroundColor = .blue + infoAction.image = UIImage(named: "icon-info") + + $0.leadingSwipe.actions = [infoAction] + $0.leadingSwipe.performsFirstActionWithFullSwipe = true + } +``` + +Swipe Actions need `tableView.isEditing` be set to `false`. Eureka will set this to `true` if there is a MultivaluedSection in the form (in the `viewWillAppear`). +If you have both MultivaluedSections and swipe actions in the same form you should set `isEditing` according to your needs. + ## Custom rows It is very common that you need a row that is different from those included in Eureka. If this is the case you will have to create your own row but this should not be difficult. You can read [this tutorial on how to create custom rows](https://blog.xmartlabs.com/2016/09/06/Eureka-custom-row-tutorial/) to get started. You might also want to have a look at [EurekaCommunity] which includes some extra rows ready to be added to Eureka. @@ -598,21 +715,75 @@ public override func customDidSelect() { To create a custom Presenter row you must create a class that conforms the `PresenterRowType` protocol. It is highly recommended to subclass `SelectorRow` as it does conform to that protocol and adds other useful functionality. -The PresenterRowType protocol is defined as followes: +The PresenterRowType protocol is defined as follows: ```swift public protocol PresenterRowType: TypedRowType { - typealias ProviderType : UIViewController, TypedRowControllerType - var presentationMode: PresentationMode? { get set } - var onPresentCallback: ((FormViewController, ProviderType)->())? { get set } + + associatedtype PresentedControllerType : UIViewController, TypedRowControllerType + + /// Defines how the view controller will be presented, pushed, etc. + var presentationMode: PresentationMode? { get set } + + /// Will be called before the presentation occurs. + var onPresentCallback: ((FormViewController, PresentedControllerType) -> Void)? { get set } } ``` -The onPresentCallback will be called when the row is about to present another view controller. This is done in the `SelectorRow` so if you do not sublass it you will have to call it yourself. +The onPresentCallback will be called when the row is about to present another view controller. This is done in the `SelectorRow` so if you do not subclass it you will have to call it yourself. The `presentationMode` is what defines how the controller is presented and which controller is presented. This presentation can be using a Segue identifier, a segue class, presenting a controller modally or pushing to a specific view controller. For example a CustomPushRow can be defined like this: + +Let's see an example.. + ```swift -public final class CustomPushRow: SelectorRow, SelectorViewController>, RowType { + +/// Generic row type where a user must select a value among several options. +open class SelectorRow: OptionsRow, PresenterRowType where Cell: BaseCell { + + + /// Defines how the view controller will be presented, pushed, etc. + open var presentationMode: PresentationMode>>? + + /// Will be called before the presentation occurs. + open var onPresentCallback: ((FormViewController, SelectorViewController>) -> Void)? + + required public init(tag: String?) { + super.init(tag: tag) + } + + /** + Extends `didSelect` method + */ + open override func customDidSelect() { + super.customDidSelect() + guard let presentationMode = presentationMode, !isDisabled else { return } + if let controller = presentationMode.makeController() { + controller.row = self + controller.title = selectorTitle ?? controller.title + onPresentCallback?(cell.formViewController()!, controller) + presentationMode.present(controller, row: self, presentingController: self.cell.formViewController()!) + } else { + presentationMode.present(nil, row: self, presentingController: self.cell.formViewController()!) + } + } + + /** + Prepares the pushed row setting its title and completion callback. + */ + open override func prepare(for segue: UIStoryboardSegue) { + super.prepare(for: segue) + guard let rowVC = segue.destination as Any as? SelectorViewController> else { return } + rowVC.title = selectorTitle ?? rowVC.title + rowVC.onDismissCallback = presentationMode?.onDismissCallback ?? rowVC.onDismissCallback + onPresentCallback?(cell.formViewController()!, rowVC) + rowVC.row = self + } +} + + +// SelectorRow conforms to PresenterRowType +public final class CustomPushRow: SelectorRow>, RowType { public required init(tag: String?) { super.init(tag: tag) @@ -625,7 +796,6 @@ public final class CustomPushRow: SelectorRow, } ``` -You can place your own UIViewController instead of `SelectorViewController` and your own cell instead of `PushSelectorCell`. ### Subclassing cells using the same row @@ -660,12 +830,10 @@ There are some things to consider when you do this:

-
Button Row


-
Check Row


@@ -676,12 +844,10 @@ There are some things to consider when you do this:

-
Slider Row


-
Stepper Row


@@ -818,16 +984,14 @@ Like PushRow but allows the selection of multiple options. -
Segmented Row (w/Title)
-
Picker Row

Presents options of a generic type through a picker view -
(There is also Picker Inline Row) +
(There is also Picker Inline Row)
@@ -846,8 +1010,6 @@ Let us know about it, we would be glad to mention it here. :) [CocoaPods](https://cocoapods.org/) is a dependency manager for Cocoa projects. -**Cocoapods 1.1.0.rc.3 or newer version must be used.** - Specify Eureka into your project's `Podfile`: ```ruby @@ -855,7 +1017,7 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '9.0' use_frameworks! -pod 'Eureka', '~> 2.0' +pod 'Eureka' ``` Then run the following command: @@ -871,7 +1033,7 @@ $ pod install Specify Eureka into your project's `Cartfile`: ```ogdl -github "xmartlabs/Eureka" ~> 2.0 +github "xmartlabs/Eureka" ~> 4.0 ``` #### Manually as Embedded Framework @@ -908,6 +1070,19 @@ If you use **Eureka** in your app We would love to hear about it! Drop us a line ## FAQ +#### How to change the text representation of the row value shown in the cell. + +Every row has the following property: + +```swift +/// Block variable used to get the String that should be displayed for the value of this row. +public var displayValueFor: ((T?) -> String?)? = { + return $0.map { String(describing: $0) } +} +``` + +You can set `displayValueFor` according the string value you want to display. + #### How to get a Row using its tag value We can get a particular row by invoking any of the following functions exposed by the `Form` class: @@ -924,7 +1099,7 @@ For instance: let dateRow : DateRow? = form.rowBy(tag: "dateRowTag") let labelRow: LabelRow? = form.rowBy(tag: "labelRowTag") -let dateRow2: Row? = form.rowBy(tag: "dateRowTag") +let dateRow2: Row? = form.rowBy(tag: "dateRowTag") let labelRow2: BaseRow? = form.rowBy(tag: "labelRowTag") ``` @@ -966,8 +1141,6 @@ Look at this [issue](https://github.com/xmartlabs/Eureka/issues/96). * Set up a new header/footer data .... ```swift -section.header = "Header Title" // use string literal as a header/footer data. HeaderFooterView conforms to ExpressibleByStringLiteral. -//or section.header = HeaderFooterView(title: "Header title \(variable)") // use String interpolation //or var header = HeaderFooterView(.class) // most flexible way to set up a header using any view type @@ -984,6 +1157,30 @@ section.header = header section.reload() ``` +#### How to customize Selector and MultipleSelector option cells + +`selectableRowSetup`, `selectableRowCellUpdate` and `selectableRowCellSetup` properties are provided to be able to customize SelectorViewController and MultipleSelectorViewController selectable cells. + +```swift +let row = PushRow() { + $0.title = "PushRow" + $0.options = [💁🏻, 🍐, 👦🏼, 🐗, 🐼, 🐻] + $0.value = 👦🏼 + $0.selectorTitle = "Choose an Emoji!" + }.onPresent { from, to in + to.dismissOnSelection = false + to.dismissOnChange = false + to.selectableRowSetup = { row in + row.cellProvider = CellProvider>(nibName: "EmojiCell", bundle: Bundle.main) + } + to.selectableRowCellUpdate = { cell, row in + cell.textLabel?.text = "Text " + row.selectableValue! // customization + cell.detailTextLabel?.text = "Detail " + row.selectableValue! + } + } + +``` + #### Don't want to use Eureka custom operators? As we've said `Form` and `Section` types conform to `MutableCollection` and `RangeReplaceableCollection`. A Form is a collection of Sections and a Section is a collection of Rows. @@ -992,16 +1189,16 @@ As we've said `Form` and `Section` types conform to `MutableCollection` and `Ran ```swift extension RangeReplaceableCollection { - public mutating func append(newElement: Self.Generator.Element) - public mutating func appendContentsOf(newElements: S) - public mutating func insert(newElement: Self.Generator.Element, atIndex i: Self.Index) - public mutating func insertContentsOf(newElements: C, at i: Self.Index) - public mutating func removeAtIndex(index: Self.Index) -> Self.Generator.Element - public mutating func removeRange(subRange: Range) - public mutating func removeFirst(n: Int) - public mutating func removeFirst() -> Self.Generator.Element - public mutating func removeAll(keepCapacity keepCapacity: Bool = default) - public mutating func reserveCapacity(n: Self.Index.Distance) + public mutating func append(_ newElement: Self.Element) + public mutating func append(contentsOf newElements: S) where S : Sequence, Self.Element == S.Element + public mutating func insert(_ newElement: Self.Element, at i: Self.Index) + public mutating func insert(contentsOf newElements: S, at i: Self.Index) where S : Collection, Self.Element == S.Element + public mutating func remove(at i: Self.Index) -> Self.Element + public mutating func removeSubrange(_ bounds: Range) + public mutating func removeFirst(_ n: Int) + public mutating func removeFirst() -> Self.Element + public mutating func removeAll(keepingCapacity keepCapacity: Bool) + public mutating func reserveCapacity(_ n: Self.IndexDistance) } ``` @@ -1013,8 +1210,8 @@ public func +++(left: Form, right: Section) -> Form { return left } -public func +=< C : Collection where C.Generator.Element == Section>(inout lhs: Form, rhs: C){ - lhs.appendContentsOf(rhs) +public func +=(inout lhs: Form, rhs: C) where C.Element == Section { + lhs.append(contentsOf: rhs) } public func <<<(left: Section, right: BaseRow) -> Section { @@ -1022,8 +1219,8 @@ public func <<<(left: Section, right: BaseRow) -> Section { return left } -public func +=< C : Collection where C.Generator.Element == BaseRow>(inout lhs: Section, rhs: C){ - lhs.appendContentsOf(rhs) +public func +=(inout lhs: Section, rhs: C) where C.Element == BaseRow { + lhs.append(contentsOf: rhs) } ``` @@ -1058,11 +1255,9 @@ It's up to you to decide if you want to use Eureka custom operators or not. [FAQ]: #faq [List sections]: #list-sections +[Multivalued sections]: #multivalued-sections [Validations]: #validations - -* [Installation] -* [FAQ] - +[Swipe Actions]: #swipe-actions [CustomCellsController]: Example/Example/ViewController.swift diff --git a/Pods/Eureka/Source/Core/BaseRow.swift b/Pods/Eureka/Source/Core/BaseRow.swift index b31b051..919447b 100644 --- a/Pods/Eureka/Source/Core/BaseRow.swift +++ b/Pods/Eureka/Source/Core/BaseRow.swift @@ -24,18 +24,20 @@ import Foundation -open class BaseRow : BaseRowType { - - var callbackOnChange: (()-> Void)? - var callbackCellUpdate: (()-> Void)? +open class BaseRow: BaseRowType { + + var callbackOnChange: (() -> Void)? + var callbackCellUpdate: (() -> Void)? var callbackCellSetup: Any? - var callbackCellOnSelection: (()-> Void)? + var callbackCellOnSelection: (() -> Void)? var callbackOnExpandInlineRow: Any? var callbackOnCollapseInlineRow: Any? - var callbackOnCellHighlightChanged: (()-> Void)? + var callbackOnCellHighlightChanged: (() -> Void)? var callbackOnRowValidationChanged: (() -> Void)? var _inlineRow: BaseRow? - + + var _cachedOptionsData: Any? + public var validationOptions: ValidationOptions = .validatesOnBlur // validation state public internal(set) var validationErrors = [ValidationError]() { @@ -46,75 +48,90 @@ open class BaseRow : BaseRowType { updateCell() } } - + public internal(set) var wasBlurred = false public internal(set) var wasChanged = false - - + public var isValid: Bool { return validationErrors.isEmpty } public var isHighlighted: Bool = false - + /// The title will be displayed in the textLabel of the row. public var title: String? - + /// Parameter used when creating the cell for this row. public var cellStyle = UITableViewCellStyle.value1 - + /// String that uniquely identifies a row. Must be unique among rows and sections. public var tag: String? - + /// The untyped cell associated to this row. public var baseCell: BaseCell! { return nil } - + /// The untyped value of this row. public var baseValue: Any? { set {} get { return nil } } - + public func validate() -> [ValidationError] { return [] } - + public static var estimatedRowHeight: CGFloat = 44.0 - + /// Condition that determines if the row should be disabled or not. - public var disabled : Condition? { + public var disabled: Condition? { willSet { removeFromDisabledRowObservers() } - didSet { addToDisabledRowObservers() } + didSet { addToDisabledRowObservers() } } - + /// Condition that determines if the row should be hidden or not. - public var hidden : Condition? { + public var hidden: Condition? { willSet { removeFromHiddenRowObservers() } - didSet { addToHiddenRowObservers() } + didSet { addToHiddenRowObservers() } } - + /// Returns if this row is currently disabled or not - public var isDisabled : Bool { return disabledCache } - + public var isDisabled: Bool { return disabledCache } + /// Returns if this row is currently hidden or not - public var isHidden : Bool { return hiddenCache } - + public var isHidden: Bool { return hiddenCache } + /// The section to which this row belongs. - public weak var section: Section? + open weak var section: Section? + + public lazy var trailingSwipe = SwipeConfiguration(self) + + //needs the accessor because if marked directly this throws "Stored properties cannot be marked potentially unavailable with '@available'" + private lazy var _leadingSwipe = SwipeConfiguration(self) + + @available(iOS 11,*) + public var leadingSwipe: SwipeConfiguration{ + get { return self._leadingSwipe } + set { self._leadingSwipe = newValue } + } - public required init(tag: String? = nil){ + public required init(tag: String? = nil) { self.tag = tag } - + /** Method that reloads the cell */ open func updateCell() {} - + /** Method called when the cell belonging to this row was selected. Must call the corresponding method in its cell. */ open func didSelect() {} - + open func prepare(for segue: UIStoryboardSegue) {} - + + /** + Helps to pick destination part of the cell after scrolling + */ + open var destinationScrollPosition: UITableViewScrollPosition = .bottom + /** Returns the IndexPath where this row is in the current form. */ @@ -122,7 +139,7 @@ open class BaseRow : BaseRowType { guard let sectionIndex = section?.index, let rowIndex = section?.index(of: self) else { return nil } return IndexPath(row: rowIndex, section: sectionIndex) } - + var hiddenCache = false var disabledCache = false { willSet { @@ -134,40 +151,46 @@ open class BaseRow : BaseRowType { } extension BaseRow { - + // Reset validation + public func cleanValidationErrors(){ + validationErrors = [] + } +} + +extension BaseRow { + /** Evaluates if the row should be hidden or not and updates the form accordingly */ public final func evaluateHidden() { guard let h = hidden, let form = section?.form else { return } switch h { - case .function(_ , let callback): + case .function(_, let callback): hiddenCache = callback(form) case .predicate(let predicate): hiddenCache = predicate.evaluate(with: self, substitutionVariables: form.dictionaryValuesToEvaluatePredicate()) } if hiddenCache { section?.hide(row: self) - } - else{ + } else { section?.show(row: self) } } - + /** Evaluates if the row should be disabled or not and updates it accordingly */ public final func evaluateDisabled() { guard let d = disabled, let form = section?.form else { return } switch d { - case .function(_ , let callback): + case .function(_, let callback): disabledCache = callback(form) case .predicate(let predicate): disabledCache = predicate.evaluate(with: self, substitutionVariables: form.dictionaryValuesToEvaluatePredicate()) } updateCell() } - + final func wasAddedTo(section: Section) { self.section = section if let t = tag { @@ -179,7 +202,7 @@ extension BaseRow { evaluateHidden() evaluateDisabled() } - + final func addToHiddenRowObservers() { guard let h = hidden else { return } switch h { @@ -189,7 +212,7 @@ extension BaseRow { section?.form?.addRowObservers(to: self, rowTags: predicate.predicateVars, type: .hidden) } } - + final func addToDisabledRowObservers() { guard let d = disabled else { return } switch d { @@ -199,13 +222,13 @@ extension BaseRow { section?.form?.addRowObservers(to: self, rowTags: predicate.predicateVars, type: .disabled) } } - - final func addToRowObservers(){ + + final func addToRowObservers() { addToHiddenRowObservers() addToDisabledRowObservers() } - - final func willBeRemovedFromForm(){ + + final func willBeRemovedFromForm() { (self as? BaseInlineRowType)?.collapseInlineRow() if let t = tag { section?.form?.rowsByTag[t] = nil @@ -213,8 +236,12 @@ extension BaseRow { } removeFromRowObservers() } - - + + final func willBeRemovedFromSection() { + willBeRemovedFromForm() + section = nil + } + final func removeFromHiddenRowObservers() { guard let h = hidden else { return } switch h { @@ -224,7 +251,7 @@ extension BaseRow { section?.form?.removeRowObservers(from: self, rowTags: predicate.predicateVars, type: .hidden) } } - + final func removeFromDisabledRowObservers() { guard let d = disabled else { return } switch d { @@ -234,8 +261,8 @@ extension BaseRow { section?.form?.removeRowObservers(from: self, rowTags: predicate.predicateVars, type: .disabled) } } - - final func removeFromRowObservers(){ + + final func removeFromRowObservers() { removeFromHiddenRowObservers() removeFromDisabledRowObservers() } @@ -243,20 +270,19 @@ extension BaseRow { extension BaseRow: Equatable, Hidable, Disableable {} - extension BaseRow { - + public func reload(with rowAnimation: UITableViewRowAnimation = .none) { guard let tableView = baseCell?.formViewController()?.tableView ?? (section?.form?.delegate as? FormViewController)?.tableView, let indexPath = indexPath else { return } tableView.reloadRows(at: [indexPath], with: rowAnimation) } - + public func deselect(animated: Bool = true) { guard let indexPath = indexPath, let tableView = baseCell?.formViewController()?.tableView ?? (section?.form?.delegate as? FormViewController)?.tableView else { return } tableView.deselectRow(at: indexPath, animated: animated) } - + public func select(animated: Bool = false, scrollPosition: UITableViewScrollPosition = .none) { guard let indexPath = indexPath, let tableView = baseCell?.formViewController()?.tableView ?? (section?.form?.delegate as? FormViewController)?.tableView else { return } @@ -264,6 +290,6 @@ extension BaseRow { } } -public func ==(lhs: BaseRow, rhs: BaseRow) -> Bool{ +public func == (lhs: BaseRow, rhs: BaseRow) -> Bool { return lhs === rhs } diff --git a/Pods/Eureka/Source/Core/Cell.swift b/Pods/Eureka/Source/Core/Cell.swift index da2fd15..af26ff7 100644 --- a/Pods/Eureka/Source/Core/Cell.swift +++ b/Pods/Eureka/Source/Core/Cell.swift @@ -22,17 +22,16 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. - import Foundation /// Base class for the Eureka cells -open class BaseCell : UITableViewCell, BaseCellType { +open class BaseCell: UITableViewCell, BaseCellType { /// Untyped row associated to this cell. public var baseRow: BaseRow! { return nil } /// Block that returns the height for this cell. - public var height: (()->CGFloat)? + public var height: (() -> CGFloat)? public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) @@ -46,7 +45,7 @@ open class BaseCell : UITableViewCell, BaseCellType { Function that returns the FormViewController this cell belongs to. */ public func formViewController() -> FormViewController? { - var responder : AnyObject? = self + var responder: AnyObject? = self while responder != nil { if let formVC = responder as? FormViewController { return formVC @@ -56,7 +55,7 @@ open class BaseCell : UITableViewCell, BaseCellType { return nil } - open func setup(){} + open func setup() {} open func update() {} open func didSelect() {} @@ -86,16 +85,18 @@ open class BaseCell : UITableViewCell, BaseCellType { } /// Generic class that represents the Eureka cells. -open class Cell : BaseCell, TypedCellType { +open class Cell: BaseCell, TypedCellType where T: Equatable { public typealias Value = T /// The row associated to this cell - public weak var row : RowOf! + public weak var row: RowOf! + + private var updatingCellForTintColorDidChange = false /// Returns the navigationAccessoryView if it is defined or calls super if not. override open var inputAccessoryView: UIView? { - if let v = formViewController()?.inputAccessoryView(for: row){ + if let v = formViewController()?.inputAccessoryView(for: row) { return v } return super.inputAccessoryView @@ -107,20 +108,19 @@ open class Cell : BaseCell, TypedCellType { required public init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) - height = { UITableViewAutomaticDimension } } /** Function responsible for setting up the cell at creation time. */ - open override func setup(){ + open override func setup() { super.setup() } /** Function responsible for updating the cell each time it is reloaded. */ - open override func update(){ + open override func update() { super.update() textLabel?.text = row.title textLabel?.textColor = row.isDisabled ? .gray : .black @@ -133,7 +133,7 @@ open class Cell : BaseCell, TypedCellType { open override func didSelect() {} override open var canBecomeFirstResponder: Bool { - get { return false } + return false } open override func becomeFirstResponder() -> Bool { @@ -152,6 +152,17 @@ open class Cell : BaseCell, TypedCellType { return result } + open override func tintColorDidChange() { + super.tintColorDidChange() + + /* Protection from infinite recursion in case an update method changes the tintColor */ + if !updatingCellForTintColorDidChange && row != nil { + updatingCellForTintColorDidChange = true + row.updateCell() + updatingCellForTintColorDidChange = false + } + } + /// The untyped row associated to this cell. - public override var baseRow : BaseRow! { return row } + public override var baseRow: BaseRow! { return row } } diff --git a/Pods/Eureka/Source/Core/CellType.swift b/Pods/Eureka/Source/Core/CellType.swift index e989974..713037c 100644 --- a/Pods/Eureka/Source/Core/CellType.swift +++ b/Pods/Eureka/Source/Core/CellType.swift @@ -24,61 +24,57 @@ import Foundation - -//MARK: Cell Protocols +// MARK: Cell Protocols public protocol BaseCellType : class { - + /// Method that will return the height of the cell - var height : (()->CGFloat)? { get } - + var height : (() -> CGFloat)? { get } + /** Method called once when creating a cell. Responsible for setting up the cell. */ func setup() - + /** Method called each time the cell is updated (e.g. 'cellForRowAtIndexPath' is called). Responsible for updating the cell. */ func update() - + /** Method called each time the cell is selected (tapped on by the user). */ func didSelect() - + /** Called when cell is about to become first responder - returns: If the cell should become first responder. */ func cellCanBecomeFirstResponder() -> Bool - + /** Method called when the cell becomes first responder */ func cellBecomeFirstResponder(withDirection: Direction) -> Bool - + /** Method called when the cell resigns first responder */ func cellResignFirstResponder() -> Bool - + /** A reference to the controller in which the cell is displayed. */ func formViewController () -> FormViewController? } +public protocol TypedCellType: BaseCellType { -public protocol TypedCellType : BaseCellType { - associatedtype Value: Equatable - + /// The row associated to this cell. - var row : RowOf! { get set } + var row: RowOf! { get set } } public protocol CellType: TypedCellType {} - - diff --git a/Pods/Eureka/Source/Core/Core.swift b/Pods/Eureka/Source/Core/Core.swift index 6fb1a6f..8fd66cb 100644 --- a/Pods/Eureka/Source/Core/Core.swift +++ b/Pods/Eureka/Source/Core/Core.swift @@ -28,42 +28,40 @@ import UIKit // MARK: Row internal class RowDefaults { - static var cellUpdate = Dictionary Void>() - static var cellSetup = Dictionary Void>() - static var onCellHighlightChanged = Dictionary Void>() - static var rowInitialization = Dictionary Void>() - static var onRowValidationChanged = Dictionary Void>() - static var rawCellUpdate = Dictionary() - static var rawCellSetup = Dictionary() - static var rawOnCellHighlightChanged = Dictionary() - static var rawRowInitialization = Dictionary() - static var rawOnRowValidationChanged = Dictionary() + static var cellUpdate = [String: (BaseCell, BaseRow) -> Void]() + static var cellSetup = [String: (BaseCell, BaseRow) -> Void]() + static var onCellHighlightChanged = [String: (BaseCell, BaseRow) -> Void]() + static var rowInitialization = [String: (BaseRow) -> Void]() + static var onRowValidationChanged = [String: (BaseCell, BaseRow) -> Void]() + static var rawCellUpdate = [String: Any]() + static var rawCellSetup = [String: Any]() + static var rawOnCellHighlightChanged = [String: Any]() + static var rawRowInitialization = [String: Any]() + static var rawOnRowValidationChanged = [String: Any]() } - // MARK: FormCells public struct CellProvider where Cell: CellType { - + /// Nibname of the cell that will be created. public private (set) var nibName: String? - + /// Bundle from which to get the nib file. public private (set) var bundle: Bundle! - - public init(){} - - public init(nibName: String, bundle: Bundle? = nil){ + public init() {} + + public init(nibName: String, bundle: Bundle? = nil) { self.nibName = nibName self.bundle = bundle ?? Bundle(for: Cell.self) } - + /** Creates the cell with the specified style. - + - parameter cellStyle: The style with which the cell will be created. - + - returns: the cell */ func makeCell(style: UITableViewCellStyle) -> Cell { @@ -76,28 +74,28 @@ public struct CellProvider where Cell: CellType { /** Enumeration that defines how a controller should be created. - + - Callback->VCType: Creates the controller inside the specified block - NibFile: Loads a controller from a nib file in some bundle - StoryBoard: Loads the controller from a Storyboard by its storyboard id */ -public enum ControllerProvider{ - +public enum ControllerProvider { + /** * Creates the controller inside the specified block */ case callback(builder: (() -> VCType)) - + /** * Loads a controller from a nib file in some bundle */ case nibFile(name: String, bundle: Bundle?) - + /** * Loads the controller from a Storyboard by its storyboard id */ case storyBoard(storyboardId: String, storyboardName: String, bundle: Bundle?) - + func makeController() -> VCType { switch self { case .callback(let builder): @@ -111,54 +109,40 @@ public enum ControllerProvider{ } } -/** - * Responsible for the options passed to a selector view controller - */ -public struct DataProvider { - - public let arrayData: [T]? - - public init(arrayData: [T]){ - self.arrayData = arrayData - } -} - /** Defines how a controller should be presented. - + - Show?: Shows the controller with `showViewController(...)`. - PresentModally?: Presents the controller modally. - SegueName?: Performs the segue with the specified identifier (name). - SegueClass?: Performs a segue from a segue class. */ public enum PresentationMode { - + /** * Shows the controller, created by the specified provider, with `showViewController(...)`. */ - case show(controllerProvider: ControllerProvider, onDismiss: ((UIViewController)->())?) - + case show(controllerProvider: ControllerProvider, onDismiss: ((UIViewController) -> Void)?) + /** * Presents the controller, created by the specified provider, modally. */ - case presentModally(controllerProvider: ControllerProvider, onDismiss: ((UIViewController)->())?) - + case presentModally(controllerProvider: ControllerProvider, onDismiss: ((UIViewController) -> Void)?) + /** * Performs the segue with the specified identifier (name). */ - case segueName(segueName: String, onDismiss: ((UIViewController)->())?) - + case segueName(segueName: String, onDismiss: ((UIViewController) -> Void)?) + /** * Performs a segue from a segue class. */ - case segueClass(segueClass: UIStoryboardSegue.Type, onDismiss: ((UIViewController)->())?) - - - case popover(controllerProvider: ControllerProvider, onDismiss: ((UIViewController)->())?) - - - public var onDismissCallback: ((UIViewController) ->())? { - switch self{ + case segueClass(segueClass: UIStoryboardSegue.Type, onDismiss: ((UIViewController) -> Void)?) + + case popover(controllerProvider: ControllerProvider, onDismiss: ((UIViewController) -> Void)?) + + public var onDismissCallback: ((UIViewController) -> Void)? { + switch self { case .show(_, let completion): return completion case .presentModally(_, let completion): @@ -171,16 +155,15 @@ public enum PresentationMode { return completion } } - - + /** Present the view controller provided by PresentationMode. Should only be used from custom row implementation. - + - parameter viewController: viewController to present if it makes sense (normally provided by makeController method) - parameter row: associated row - parameter presentingViewController: form view controller */ - public func present(_ viewController: VCType!, row: BaseRow, presentingController: FormViewController){ + public func present(_ viewController: VCType!, row: BaseRow, presentingController: FormViewController) { switch self { case .show(_, _): presentingController.show(viewController, sender: row) @@ -199,12 +182,12 @@ public enum PresentationMode { porpoverController.sourceView = porpoverController.sourceView ?? presentingController.tableView presentingController.present(viewController, animated: true) } - + } - + /** Creates the view controller specified by presentation mode. Should only be used from custom row implementation. - + - returns: the created view controller or nil depending on the PresentationMode type. */ public func makeController() -> VCType? { @@ -244,7 +227,7 @@ public protocol FormatterProtocol { func getNewPosition(forPosition: UITextPosition, inTextInput textInput: UITextInput, oldValue: String?, newValue: String?) -> UITextPosition } -//MARK: Predicate Machine +// MARK: Predicate Machine enum ConditionType { case hidden, disabled @@ -252,7 +235,7 @@ enum ConditionType { /** Enumeration that are used to specify the disbaled and hidden conditions of rows - + - Function: A function that calculates the result - Predicate: A predicate that returns the result */ @@ -266,7 +249,7 @@ public enum Condition { * @return If the condition is true or false */ case function([String], (Form)->Bool) - + /** * Calculate the condition using a NSPredicate * @@ -278,31 +261,31 @@ public enum Condition { } extension Condition : ExpressibleByBooleanLiteral { - + /** Initialize a condition to return afixed boolean value always */ - public init(booleanLiteral value: Bool){ + public init(booleanLiteral value: Bool) { self = Condition.function([]) { _ in return value } } } extension Condition : ExpressibleByStringLiteral { - + /** Initialize a Condition with a string that will be converted to a NSPredicate */ - public init(stringLiteral value: String){ + public init(stringLiteral value: String) { self = .predicate(NSPredicate(format: value)) } - + /** Initialize a Condition with a string that will be converted to a NSPredicate */ public init(unicodeScalarLiteral value: String) { self = .predicate(NSPredicate(format: value)) } - + /** Initialize a Condition with a string that will be converted to a NSPredicate */ @@ -311,14 +294,14 @@ extension Condition : ExpressibleByStringLiteral { } } -//MARK: Errors +// MARK: Errors /** Errors thrown by Eureka - DuplicatedTag: When a section or row is inserted whose tag dows already exist */ -public enum EurekaError : Error { +public enum EurekaError: Error { case duplicatedTag(tag: String) } @@ -328,40 +311,40 @@ public enum EurekaError : Error { * A protocol implemented by FormViewController */ public protocol FormViewControllerProtocol { - var tableView: UITableView? { get } - - func beginEditing(of: Cell) - func endEditing(of: Cell) - + var tableView: UITableView! { get } + + func beginEditing(of: Cell) + func endEditing(of: Cell) + func insertAnimation(forRows rows: [BaseRow]) -> UITableViewRowAnimation func deleteAnimation(forRows rows: [BaseRow]) -> UITableViewRowAnimation func reloadAnimation(oldRows: [BaseRow], newRows: [BaseRow]) -> UITableViewRowAnimation - func insertAnimation(forSections sections : [Section]) -> UITableViewRowAnimation - func deleteAnimation(forSections sections : [Section]) -> UITableViewRowAnimation - func reloadAnimation(oldSections: [Section], newSections:[Section]) -> UITableViewRowAnimation + func insertAnimation(forSections sections: [Section]) -> UITableViewRowAnimation + func deleteAnimation(forSections sections: [Section]) -> UITableViewRowAnimation + func reloadAnimation(oldSections: [Section], newSections: [Section]) -> UITableViewRowAnimation } /** * Navigation options for a form view controller. */ -public struct RowNavigationOptions : OptionSet { - - private enum NavigationOptions : Int { +public struct RowNavigationOptions: OptionSet { + + private enum NavigationOptions: Int { case disabled = 0, enabled = 1, stopDisabledRow = 2, skipCanNotBecomeFirstResponderRow = 4 } public let rawValue: Int - public init(rawValue: Int){ self.rawValue = rawValue} - private init(_ options:NavigationOptions ){ self.rawValue = options.rawValue } - + public init(rawValue: Int) { self.rawValue = rawValue} + private init(_ options: NavigationOptions ) { self.rawValue = options.rawValue } + /// No navigation. public static let Disabled = RowNavigationOptions(.disabled) - + /// Full navigation. public static let Enabled = RowNavigationOptions(.enabled) - + /// Break navigation when next row is disabled. public static let StopDisabledRow = RowNavigationOptions(.stopDisabledRow) - + /// Break navigation when next row cannot become first responder. public static let SkipCanNotBecomeFirstResponderRow = RowNavigationOptions(.skipCanNotBecomeFirstResponderRow) } @@ -372,13 +355,13 @@ public struct RowNavigationOptions : OptionSet { public struct KeyboardReturnTypeConfiguration { /// Used when the next row is available. public var nextKeyboardType = UIReturnKeyType.next - + /// Used if next row is not available. public var defaultKeyboardType = UIReturnKeyType.default - - public init(){} - - public init(nextKeyboardType: UIReturnKeyType, defaultKeyboardType: UIReturnKeyType){ + + public init() {} + + public init(nextKeyboardType: UIReturnKeyType, defaultKeyboardType: UIReturnKeyType) { self.nextKeyboardType = nextKeyboardType self.defaultKeyboardType = defaultKeyboardType } @@ -387,37 +370,37 @@ public struct KeyboardReturnTypeConfiguration { /** * Options that define when an inline row should collapse. */ -public struct InlineRowHideOptions : OptionSet { - - private enum _InlineRowHideOptions : Int { +public struct InlineRowHideOptions: OptionSet { + + private enum _InlineRowHideOptions: Int { case never = 0, anotherInlineRowIsShown = 1, firstResponderChanges = 2 } public let rawValue: Int - public init(rawValue: Int){ self.rawValue = rawValue} - private init(_ options:_InlineRowHideOptions ){ self.rawValue = options.rawValue } - + public init(rawValue: Int) { self.rawValue = rawValue} + private init(_ options: _InlineRowHideOptions ) { self.rawValue = options.rawValue } + /// Never collapse automatically. Only when user taps inline row. public static let Never = InlineRowHideOptions(.never) - + /// Collapse qhen another inline row expands. Just one inline row will be expanded at a time. public static let AnotherInlineRowIsShown = InlineRowHideOptions(.anotherInlineRowIsShown) - + /// Collapse when first responder changes. public static let FirstResponderChanges = InlineRowHideOptions(.firstResponderChanges) } /// View controller that shows a form. -open class FormViewController : UIViewController, FormViewControllerProtocol { - - @IBOutlet public var tableView: UITableView? - - private lazy var _form : Form = { [weak self] in +open class FormViewController: UIViewController, FormViewControllerProtocol, FormDelegate { + + @IBOutlet public var tableView: UITableView! + + private lazy var _form: Form = { [weak self] in let form = Form() form.delegate = self return form }() - - public var form : Form { + + public var form: Form { get { return _form } set { guard form !== newValue else { return } @@ -430,105 +413,112 @@ open class FormViewController : UIViewController, FormViewControllerProtocol { } } } - + /// Extra space to leave between between the row in focus and the keyboard open var rowKeyboardSpacing: CGFloat = 0 - + /// Enables animated scrolling on row navigation open var animateScroll = false - + /// Accessory view that is responsible for the navigation between rows - open var navigationAccessoryView : NavigationAccessoryView! - + open var navigationAccessoryView: NavigationAccessoryView! + /// Defines the behaviour of the navigation between rows - public var navigationOptions : RowNavigationOptions? + public var navigationOptions: RowNavigationOptions? private var tableViewStyle: UITableViewStyle = .grouped - + public init(style: UITableViewStyle) { super.init(nibName: nil, bundle: nil) tableViewStyle = style } - + public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) } - + public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } - + open override func viewDidLoad() { super.viewDidLoad() navigationAccessoryView = NavigationAccessoryView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 44.0)) navigationAccessoryView.autoresizingMask = .flexibleWidth - + if tableView == nil { tableView = UITableView(frame: view.bounds, style: tableViewStyle) - tableView?.autoresizingMask = UIViewAutoresizing.flexibleWidth.union(.flexibleHeight) - if #available(iOS 9.0, *){ - tableView?.cellLayoutMarginsFollowReadableWidth = false + tableView.autoresizingMask = UIViewAutoresizing.flexibleWidth.union(.flexibleHeight) + if #available(iOS 9.0, *) { + tableView.cellLayoutMarginsFollowReadableWidth = false } } - if tableView?.superview == nil { - view.addSubview(tableView!) + if tableView.superview == nil { + view.addSubview(tableView) } - if tableView?.delegate == nil { - tableView?.delegate = self + if tableView.delegate == nil { + tableView.delegate = self } - if tableView?.dataSource == nil { - tableView?.dataSource = self + if tableView.dataSource == nil { + tableView.dataSource = self } - tableView?.estimatedRowHeight = BaseRow.estimatedRowHeight + tableView.rowHeight = UITableViewAutomaticDimension + tableView.estimatedRowHeight = BaseRow.estimatedRowHeight + tableView.allowsSelectionDuringEditing = true } - + open override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - - let selectedIndexPaths = tableView?.indexPathsForSelectedRows ?? [] - tableView?.reloadRows(at: selectedIndexPaths, with: .none) + animateTableView = true + let selectedIndexPaths = tableView.indexPathsForSelectedRows ?? [] + if !selectedIndexPaths.isEmpty { + tableView.reloadRows(at: selectedIndexPaths, with: .none) + } selectedIndexPaths.forEach { - tableView?.selectRow(at: $0, animated: false, scrollPosition: .none) + tableView.selectRow(at: $0, animated: false, scrollPosition: .none) } let deselectionAnimation = { [weak self] (context: UIViewControllerTransitionCoordinatorContext) in selectedIndexPaths.forEach { - self?.tableView?.deselectRow(at: $0, animated: context.isAnimated) + self?.tableView.deselectRow(at: $0, animated: context.isAnimated) } } let reselection = { [weak self] (context: UIViewControllerTransitionCoordinatorContext) in if context.isCancelled { selectedIndexPaths.forEach { - self?.tableView?.selectRow(at: $0, animated: false, scrollPosition: .none) + self?.tableView.selectRow(at: $0, animated: false, scrollPosition: .none) } } } if let coordinator = transitionCoordinator { coordinator.animate(alongsideTransition: deselectionAnimation, completion: reselection) - } - else { + } else { selectedIndexPaths.forEach { - tableView?.deselectRow(at: $0, animated: false) + tableView.deselectRow(at: $0, animated: false) } } NotificationCenter.default.addObserver(self, selector: #selector(FormViewController.keyboardWillShow(_:)), name: Notification.Name.UIKeyboardWillShow, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(FormViewController.keyboardWillHide(_:)), name: Notification.Name.UIKeyboardWillHide, object: nil) + + if form.containsMultivaluedSection && (isBeingPresented || isMovingToParentViewController) { + tableView.setEditing(true, animated: false) + } } - - open override func viewDidDisappear(_ animated: Bool) { - super.viewDidDisappear(animated) + + open override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) NotificationCenter.default.removeObserver(self, name: Notification.Name.UIKeyboardWillShow, object: nil) NotificationCenter.default.removeObserver(self, name: Notification.Name.UIKeyboardWillHide, object: nil) } - + open override func prepare(for segue: UIStoryboardSegue, sender: Any?) { super.prepare(for: segue, sender: sender) let baseRow = sender as? BaseRow baseRow?.prepare(for: segue) } - + /** Returns the navigation accessory view if it is enabled. Returns nil otherwise. */ @@ -546,13 +536,13 @@ open class FormViewController : UIViewController, FormViewControllerProtocol { navigationAccessoryView.nextButton.isEnabled = nextRow(for: row, withDirection: .down) != nil return navigationAccessoryView } - - //MARK: FormViewControllerProtocol - + + // MARK: FormViewControllerProtocol + /** Called when a cell becomes first responder */ - public final func beginEditing(of cell: Cell) { + public final func beginEditing(of cell: Cell) { cell.row.isHighlighted = true cell.row.updateCell() RowDefaults.onCellHighlightChanged["\(type(of: cell.row!))"]?(cell, cell.row) @@ -566,100 +556,105 @@ open class FormViewController : UIViewController, FormViewControllerProtocol { } } } - + /** Called when a cell resigns first responder */ - public final func endEditing(of cell: Cell) { + public final func endEditing(of cell: Cell) { cell.row.isHighlighted = false cell.row.wasBlurred = true - RowDefaults.onCellHighlightChanged["\(type(of: self))"]?(cell, cell.row) + RowDefaults.onCellHighlightChanged["\(type(of: cell.row!))"]?(cell, cell.row) cell.row.callbackOnCellHighlightChanged?() if cell.row.validationOptions.contains(.validatesOnBlur) || (cell.row.wasChanged && cell.row.validationOptions.contains(.validatesOnChangeAfterBlurred)) { cell.row.validate() } cell.row.updateCell() } - + /** Returns the animation for the insertion of the given rows. */ open func insertAnimation(forRows rows: [BaseRow]) -> UITableViewRowAnimation { return .fade } - + /** Returns the animation for the deletion of the given rows. */ open func deleteAnimation(forRows rows: [BaseRow]) -> UITableViewRowAnimation { return .fade } - + /** Returns the animation for the reloading of the given rows. */ open func reloadAnimation(oldRows: [BaseRow], newRows: [BaseRow]) -> UITableViewRowAnimation { return .automatic } - + /** Returns the animation for the insertion of the given sections. */ open func insertAnimation(forSections sections: [Section]) -> UITableViewRowAnimation { return .automatic } - + /** Returns the animation for the deletion of the given sections. */ open func deleteAnimation(forSections sections: [Section]) -> UITableViewRowAnimation { return .automatic } - + /** Returns the animation for the reloading of the given sections. */ open func reloadAnimation(oldSections: [Section], newSections: [Section]) -> UITableViewRowAnimation { return .automatic } - - //MARK: TextField and TextView Delegate - + + // MARK: TextField and TextView Delegate + open func textInputShouldBeginEditing(_ textInput: UITextInput, cell: Cell) -> Bool { return true } - + open func textInputDidBeginEditing(_ textInput: UITextInput, cell: Cell) { if let row = cell.row as? KeyboardReturnHandler { let next = nextRow(for: cell.row, withDirection: .down) if let textField = textInput as? UITextField { - textField.returnKeyType = next != nil ? (row.keyboardReturnType?.nextKeyboardType ?? (form.keyboardReturnType?.nextKeyboardType ?? Form.defaultKeyboardReturnType.nextKeyboardType )) : (row.keyboardReturnType?.defaultKeyboardType ?? (form.keyboardReturnType?.defaultKeyboardType ?? Form.defaultKeyboardReturnType.defaultKeyboardType)) - } - else if let textView = textInput as? UITextView { - textView.returnKeyType = next != nil ? (row.keyboardReturnType?.nextKeyboardType ?? (form.keyboardReturnType?.nextKeyboardType ?? Form.defaultKeyboardReturnType.nextKeyboardType )) : (row.keyboardReturnType?.defaultKeyboardType ?? (form.keyboardReturnType?.defaultKeyboardType ?? Form.defaultKeyboardReturnType.defaultKeyboardType)) + textField.returnKeyType = next != nil ? (row.keyboardReturnType?.nextKeyboardType ?? + (form.keyboardReturnType?.nextKeyboardType ?? Form.defaultKeyboardReturnType.nextKeyboardType )) : + (row.keyboardReturnType?.defaultKeyboardType ?? (form.keyboardReturnType?.defaultKeyboardType ?? + Form.defaultKeyboardReturnType.defaultKeyboardType)) + } else if let textView = textInput as? UITextView { + textView.returnKeyType = next != nil ? (row.keyboardReturnType?.nextKeyboardType ?? + (form.keyboardReturnType?.nextKeyboardType ?? Form.defaultKeyboardReturnType.nextKeyboardType )) : + (row.keyboardReturnType?.defaultKeyboardType ?? (form.keyboardReturnType?.defaultKeyboardType ?? + Form.defaultKeyboardReturnType.defaultKeyboardType)) } } } - + open func textInputShouldEndEditing(_ textInput: UITextInput, cell: Cell) -> Bool { return true } - + open func textInputDidEndEditing(_ textInput: UITextInput, cell: Cell) { - + } - + open func textInput(_ textInput: UITextInput, shouldChangeCharactersInRange range: NSRange, replacementString string: String, cell: Cell) -> Bool { return true } - + open func textInputShouldClear(_ textInput: UITextInput, cell: Cell) -> Bool { return true } open func textInputShouldReturn(_ textInput: UITextInput, cell: Cell) -> Bool { - if let nextRow = nextRow(for: cell.row, withDirection: .down){ - if nextRow.baseCell.cellCanBecomeFirstResponder(){ + if let nextRow = nextRow(for: cell.row, withDirection: .down) { + if nextRow.baseCell.cellCanBecomeFirstResponder() { nextRow.baseCell.cellBecomeFirstResponder() return true } @@ -667,24 +662,111 @@ open class FormViewController : UIViewController, FormViewControllerProtocol { tableView?.endEditing(true) return true } - - //MARK: FormDelegate - + + // MARK: FormDelegate + open func valueHasBeenChanged(for: BaseRow, oldValue: Any?, newValue: Any?) {} - - //MARK: Private - - var oldBottomInset : CGFloat? + + // MARK: UITableViewDelegate + + @objc open func tableView(_ tableView: UITableView, willBeginReorderingRowAtIndexPath indexPath: IndexPath) { + // end editing if inline cell is first responder + let row = form[indexPath] + if let inlineRow = row as? BaseInlineRowType, row._inlineRow != nil { + inlineRow.collapseInlineRow() + } + } + + // MARK: FormDelegate + + open func sectionsHaveBeenAdded(_ sections: [Section], at indexes: IndexSet) { + guard animateTableView else { return } + tableView?.beginUpdates() + tableView?.insertSections(indexes, with: insertAnimation(forSections: sections)) + tableView?.endUpdates() + } + + open func sectionsHaveBeenRemoved(_ sections: [Section], at indexes: IndexSet) { + guard animateTableView else { return } + tableView?.beginUpdates() + tableView?.deleteSections(indexes, with: deleteAnimation(forSections: sections)) + tableView?.endUpdates() + } + + open func sectionsHaveBeenReplaced(oldSections: [Section], newSections: [Section], at indexes: IndexSet) { + guard animateTableView else { return } + tableView?.beginUpdates() + tableView?.reloadSections(indexes, with: reloadAnimation(oldSections: oldSections, newSections: newSections)) + tableView?.endUpdates() + } + + open func rowsHaveBeenAdded(_ rows: [BaseRow], at indexes: [IndexPath]) { + guard animateTableView else { return } + tableView?.beginUpdates() + tableView?.insertRows(at: indexes, with: insertAnimation(forRows: rows)) + tableView?.endUpdates() + } + + open func rowsHaveBeenRemoved(_ rows: [BaseRow], at indexes: [IndexPath]) { + guard animateTableView else { return } + tableView?.beginUpdates() + tableView?.deleteRows(at: indexes, with: deleteAnimation(forRows: rows)) + tableView?.endUpdates() + } + + open func rowsHaveBeenReplaced(oldRows: [BaseRow], newRows: [BaseRow], at indexes: [IndexPath]) { + guard animateTableView else { return } + tableView?.beginUpdates() + tableView?.reloadRows(at: indexes, with: reloadAnimation(oldRows: oldRows, newRows: newRows)) + tableView?.endUpdates() + } + + // MARK: Private + + var oldBottomInset: CGFloat? + var animateTableView = false + + /** Calculates the height needed for a header or footer. */ + fileprivate func height(specifiedHeight: (() -> CGFloat)?, sectionView: UIView?, sectionTitle: String?) -> CGFloat { + if let height = specifiedHeight { + return height() + } + + if let sectionView = sectionView { + let height = sectionView.bounds.height + + if height == 0 { + return UITableViewAutomaticDimension + } + + return height + } + + if let sectionTitle = sectionTitle, + sectionTitle != "" { + return UITableViewAutomaticDimension + } + + // Fix for iOS 11+. By returning 0, we ensure that no section header or + // footer is shown when self-sizing is enabled (i.e. when + // tableView.estimatedSectionHeaderHeight or tableView.estimatedSectionFooterHeight + // == UITableViewAutomaticDimension). + if tableView.style == .plain { + return 0 + } + + return UITableViewAutomaticDimension + } } extension FormViewController : UITableViewDelegate { - - //MARK: UITableViewDelegate - + + // MARK: UITableViewDelegate + open func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? { return indexPath } - + open func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard tableView == self.tableView else { return } let row = form[indexPath] @@ -694,145 +776,231 @@ extension FormViewController : UITableViewDelegate { } row.didSelect() } - + open func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { guard tableView == self.tableView else { return tableView.rowHeight } let row = form[indexPath.section][indexPath.row] return row.baseCell.height?() ?? tableView.rowHeight } - + open func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { - guard tableView == self.tableView else { return tableView.rowHeight } + guard tableView == self.tableView else { return tableView.estimatedRowHeight } let row = form[indexPath.section][indexPath.row] return row.baseCell.height?() ?? tableView.estimatedRowHeight } - + open func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { return form[section].header?.viewForSection(form[section], type: .header) } - + open func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { return form[section].footer?.viewForSection(form[section], type:.footer) } - + open func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { - if let height = form[section].header?.height { - return height() + return height(specifiedHeight: form[section].header?.height, + sectionView: self.tableView(tableView, viewForHeaderInSection: section), + sectionTitle: self.tableView(tableView, titleForHeaderInSection: section)) + } + + open func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { + return height(specifiedHeight: form[section].footer?.height, + sectionView: self.tableView(tableView, viewForFooterInSection: section), + sectionTitle: self.tableView(tableView, titleForFooterInSection: section)) + } + + open func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { + let row = form[indexPath] + guard !row.isDisabled else { return false } + if row.trailingSwipe.actions.count > 0 { return true } + if #available(iOS 11,*), row.leadingSwipe.actions.count > 0 { return true } + guard let section = form[indexPath.section] as? MultivaluedSection else { return false } + guard !(indexPath.row == section.count - 1 && section.multivaluedOptions.contains(.Insert) && section.showInsertIconInAddButton) else { + return true } - guard let view = form[section].header?.viewForSection(form[section], type: .header) else{ - return UITableViewAutomaticDimension + if indexPath.row > 0 && section[indexPath.row - 1] is BaseInlineRowType && section[indexPath.row - 1]._inlineRow != nil { + return false } - guard view.bounds.height != 0 else { - return UITableViewAutomaticDimension + return true + } + + open func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { + if editingStyle == .delete { + let row = form[indexPath] + let section = row.section! + if let _ = row.baseCell.findFirstResponder() { + tableView.endEditing(true) + } + section.remove(at: indexPath.row) + DispatchQueue.main.async { + tableView.isEditing = !tableView.isEditing + tableView.isEditing = !tableView.isEditing + } + } else if editingStyle == .insert { + guard var section = form[indexPath.section] as? MultivaluedSection else { return } + guard let multivaluedRowToInsertAt = section.multivaluedRowToInsertAt else { + fatalError("Multivalued section multivaluedRowToInsertAt property must be set up") + } + let newRow = multivaluedRowToInsertAt(max(0, section.count - 1)) + section.insert(newRow, at: section.count - 1) + DispatchQueue.main.async { + tableView.isEditing = !tableView.isEditing + tableView.isEditing = !tableView.isEditing + } + tableView.scrollToRow(at: IndexPath(row: section.count - 1, section: indexPath.section), at: .bottom, animated: true) + if newRow.baseCell.cellCanBecomeFirstResponder() { + newRow.baseCell.cellBecomeFirstResponder() + } else if let inlineRow = newRow as? BaseInlineRowType { + inlineRow.expandInlineRow() + } } - return view.bounds.height } - - open func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { - if let height = form[section].footer?.height { - return height() + + open func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { + guard let section = form[indexPath.section] as? MultivaluedSection, section.multivaluedOptions.contains(.Reorder) && section.count > 1 else { + return false } - guard let view = form[section].footer?.viewForSection(form[section], type: .footer) else{ - return UITableViewAutomaticDimension + if section.multivaluedOptions.contains(.Insert) && (section.count <= 2 || indexPath.row == (section.count - 1)) { + return false } - guard view.bounds.height != 0 else { - return UITableViewAutomaticDimension + if indexPath.row > 0 && section[indexPath.row - 1] is BaseInlineRowType && section[indexPath.row - 1]._inlineRow != nil { + return false + } + return true + } + + open func tableView(_ tableView: UITableView, targetIndexPathForMoveFromRowAt sourceIndexPath: IndexPath, toProposedIndexPath proposedDestinationIndexPath: IndexPath) -> IndexPath { + guard let section = form[sourceIndexPath.section] as? MultivaluedSection else { return sourceIndexPath } + guard sourceIndexPath.section == proposedDestinationIndexPath.section else { return sourceIndexPath } + + let destRow = form[proposedDestinationIndexPath] + if destRow is BaseInlineRowType && destRow._inlineRow != nil { + return IndexPath(row: proposedDestinationIndexPath.row + (sourceIndexPath.row < proposedDestinationIndexPath.row ? 1 : -1), section:sourceIndexPath.section) + } + + if proposedDestinationIndexPath.row > 0 { + let previousRow = form[IndexPath(row: proposedDestinationIndexPath.row - 1, section: proposedDestinationIndexPath.section)] + if previousRow is BaseInlineRowType && previousRow._inlineRow != nil { + return IndexPath(row: proposedDestinationIndexPath.row + (sourceIndexPath.row < proposedDestinationIndexPath.row ? 1 : -1), section:sourceIndexPath.section) + } + } + if section.multivaluedOptions.contains(.Insert) && proposedDestinationIndexPath.row == section.count - 1 { + return IndexPath(row: section.count - 2, section: sourceIndexPath.section) + } + return proposedDestinationIndexPath + } + + open func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { + + guard var section = form[sourceIndexPath.section] as? MultivaluedSection else { return } + if sourceIndexPath.row < section.count && destinationIndexPath.row < section.count && sourceIndexPath.row != destinationIndexPath.row { + + let sourceRow = form[sourceIndexPath] + animateTableView = false + section.remove(at: sourceIndexPath.row) + section.insert(sourceRow, at: destinationIndexPath.row) + animateTableView = true + // update the accessory view + let _ = inputAccessoryView(for: sourceRow) + } + } + + open func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle { + guard let section = form[indexPath.section] as? MultivaluedSection else { + if form[indexPath].trailingSwipe.actions.count > 0 { + return .delete + } + return .none + } + if section.multivaluedOptions.contains(.Insert) && indexPath.row == section.count - 1 { + return section.showInsertIconInAddButton ? .insert : .none } - return view.bounds.height + if section.multivaluedOptions.contains(.Delete) { + return .delete + } + return .none + } + + open func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool { + return self.tableView(tableView, editingStyleForRowAt: indexPath) != .none } + + @available(iOS 11,*) + open func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { + return form[indexPath].leadingSwipe.contextualConfiguration + } + + @available(iOS 11,*) + open func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { + return form[indexPath].trailingSwipe.contextualConfiguration + } + + open func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]?{ + guard let actions = form[indexPath].trailingSwipe.contextualActions as? [UITableViewRowAction], !actions.isEmpty else { + return nil + } + return actions + } } extension FormViewController : UITableViewDataSource { - - //MARK: UITableViewDataSource - + + // MARK: UITableViewDataSource + open func numberOfSections(in tableView: UITableView) -> Int { return form.count } - + open func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return form[section].count } - + open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { form[indexPath].updateCell() return form[indexPath].baseCell } - + open func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { return form[section].header?.title } - + open func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? { return form[section].footer?.title } -} -extension FormViewController: FormDelegate { - - - //MARK: FormDelegate - - open func sectionsHaveBeenAdded(_ sections: [Section], at indexes: IndexSet){ - tableView?.beginUpdates() - tableView?.insertSections(indexes, with: insertAnimation(forSections: sections)) - tableView?.endUpdates() - } - - open func sectionsHaveBeenRemoved(_ sections: [Section], at indexes: IndexSet){ - tableView?.beginUpdates() - tableView?.deleteSections(indexes, with: deleteAnimation(forSections: sections)) - tableView?.endUpdates() - } - - open func sectionsHaveBeenReplaced(oldSections:[Section], newSections: [Section], at indexes: IndexSet){ - tableView?.beginUpdates() - tableView?.reloadSections(indexes, with: reloadAnimation(oldSections: oldSections, newSections: newSections)) - tableView?.endUpdates() - } - - open func rowsHaveBeenAdded(_ rows: [BaseRow], at indexes: [IndexPath]) { - tableView?.beginUpdates() - tableView?.insertRows(at: indexes, with: insertAnimation(forRows: rows)) - tableView?.endUpdates() - } - - open func rowsHaveBeenRemoved(_ rows: [BaseRow], at indexes: [IndexPath]) { - tableView?.beginUpdates() - tableView?.deleteRows(at: indexes, with: deleteAnimation(forRows: rows)) - tableView?.endUpdates() + open func sectionIndexTitles(for tableView: UITableView) -> [String]? { + return nil } - open func rowsHaveBeenReplaced(oldRows:[BaseRow], newRows: [BaseRow], at indexes: [IndexPath]){ - tableView?.beginUpdates() - tableView?.reloadRows(at: indexes, with: reloadAnimation(oldRows: oldRows, newRows: newRows)) - tableView?.endUpdates() + open func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int { + return 0 } } extension FormViewController : UIScrollViewDelegate { - - //MARK: UIScrollViewDelegate - + + // MARK: UIScrollViewDelegate + open func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { - tableView?.endEditing(true) + guard let tableView = tableView, scrollView === tableView else { return } + tableView.endEditing(true) } } extension FormViewController { - - //MARK: KeyBoard Notifications - + + // MARK: KeyBoard Notifications + /** Called when the keyboard will appear. Adjusts insets of the tableView and scrolls it if necessary. */ - open func keyboardWillShow(_ notification: Notification){ + @objc open func keyboardWillShow(_ notification: Notification) { guard let table = tableView, let cell = table.findFirstResponder()?.formCell() else { return } let keyBoardInfo = notification.userInfo! let endFrame = keyBoardInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue - + let keyBoardFrame = table.window!.convert(endFrame.cgRectValue, to: table.superview) let newBottomInset = table.frame.origin.y + table.frame.size.height - keyBoardFrame.origin.y + rowKeyboardSpacing var tableInsets = table.contentInset @@ -852,12 +1020,12 @@ extension FormViewController { UIView.commitAnimations() } } - + /** Called when the keyboard will disappear. Adjusts insets of the tableView. */ - open func keyboardWillHide(_ notification: Notification){ - guard let table = tableView, let oldBottom = oldBottomInset else { return } + @objc open func keyboardWillHide(_ notification: Notification) { + guard let table = tableView, let oldBottom = oldBottomInset else { return } let keyBoardInfo = notification.userInfo! var tableInsets = table.contentInset var scrollIndicatorInsets = table.scrollIndicatorInsets @@ -876,39 +1044,39 @@ extension FormViewController { public enum Direction { case up, down } extension FormViewController { - - //MARK: Navigation Methods - - func navigationDone(_ sender: UIBarButtonItem) { + + // MARK: Navigation Methods + + @objc func navigationDone(_ sender: UIBarButtonItem) { tableView?.endEditing(true) } - - func navigationAction(_ sender: UIBarButtonItem) { + + @objc func navigationAction(_ sender: UIBarButtonItem) { navigateTo(direction: sender == navigationAccessoryView.previousButton ? .up : .down) } - - public func navigateTo(direction: Direction){ + + public func navigateTo(direction: Direction) { guard let currentCell = tableView?.findFirstResponder()?.formCell() else { return } guard let currentIndexPath = tableView?.indexPath(for: currentCell) else { assertionFailure(); return } guard let nextRow = nextRow(for: form[currentIndexPath], withDirection: direction) else { return } - if nextRow.baseCell.cellCanBecomeFirstResponder(){ + if nextRow.baseCell.cellCanBecomeFirstResponder() { tableView?.scrollToRow(at: nextRow.indexPath!, at: .none, animated: animateScroll) nextRow.baseCell.cellBecomeFirstResponder(withDirection: direction) } } - + func nextRow(for currentRow: BaseRow, withDirection direction: Direction) -> BaseRow? { - + let options = navigationOptions ?? Form.defaultNavigationOptions guard options.contains(.Enabled) else { return nil } guard let next = direction == .down ? form.nextRow(for: currentRow) : form.previousRow(for: currentRow) else { return nil } if next.isDisabled && options.contains(.StopDisabledRow) { return nil } - if !next.baseCell.cellCanBecomeFirstResponder() && !next.isDisabled && !options.contains(.SkipCanNotBecomeFirstResponderRow){ + if !next.baseCell.cellCanBecomeFirstResponder() && !next.isDisabled && !options.contains(.SkipCanNotBecomeFirstResponderRow) { return nil } - if (!next.isDisabled && next.baseCell.cellCanBecomeFirstResponder()){ + if !next.isDisabled && next.baseCell.cellCanBecomeFirstResponder() { return next } return nextRow(for: next, withDirection:direction) @@ -916,13 +1084,13 @@ extension FormViewController { } extension FormViewControllerProtocol { - - //MARK: Helpers - - func makeRowVisible(_ row: BaseRow){ + + // MARK: Helpers + + func makeRowVisible(_ row: BaseRow, destinationScrollPosition: UITableViewScrollPosition) { guard let cell = row.baseCell, let indexPath = row.indexPath, let tableView = tableView else { return } - if cell.window == nil || (tableView.contentOffset.y + tableView.frame.size.height <= cell.frame.origin.y + cell.frame.size.height){ - tableView.scrollToRow(at: indexPath, at: .bottom, animated: true) + if cell.window == nil || (tableView.contentOffset.y + tableView.frame.size.height <= cell.frame.origin.y + cell.frame.size.height) { + tableView.scrollToRow(at: indexPath, at: destinationScrollPosition, animated: true) } } } diff --git a/Pods/Eureka/Source/Core/Form.swift b/Pods/Eureka/Source/Core/Form.swift index eef7f28..b8bfd7e 100644 --- a/Pods/Eureka/Source/Core/Form.swift +++ b/Pods/Eureka/Source/Core/Form.swift @@ -22,129 +22,118 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. - import Foundation - /// The delegate of the Eureka form. public protocol FormDelegate : class { func sectionsHaveBeenAdded(_ sections: [Section], at: IndexSet) func sectionsHaveBeenRemoved(_ sections: [Section], at: IndexSet) - func sectionsHaveBeenReplaced(oldSections:[Section], newSections: [Section], at: IndexSet) - func rowsHaveBeenAdded(_ rows: [BaseRow], at:[IndexPath]) - func rowsHaveBeenRemoved(_ rows: [BaseRow], at:[IndexPath]) - func rowsHaveBeenReplaced(oldRows:[BaseRow], newRows: [BaseRow], at: [IndexPath]) - func valueHasBeenChanged(for: BaseRow, oldValue: Any?, newValue: Any?) + func sectionsHaveBeenReplaced(oldSections: [Section], newSections: [Section], at: IndexSet) + func rowsHaveBeenAdded(_ rows: [BaseRow], at: [IndexPath]) + func rowsHaveBeenRemoved(_ rows: [BaseRow], at: [IndexPath]) + func rowsHaveBeenReplaced(oldRows: [BaseRow], newRows: [BaseRow], at: [IndexPath]) + func valueHasBeenChanged(for row: BaseRow, oldValue: Any?, newValue: Any?) } - -//MARK: Form +// MARK: Form /// The class representing the Eureka form. public final class Form { - + /// Defines the default options of the navigation accessory view. public static var defaultNavigationOptions = RowNavigationOptions.Enabled.union(.SkipCanNotBecomeFirstResponderRow) - + /// The default options that define when an inline row will be hidden. Applies only when `inlineRowHideOptions` is nil. public static var defaultInlineRowHideOptions = InlineRowHideOptions.FirstResponderChanges.union(.AnotherInlineRowIsShown) - + /// The options that define when an inline row will be hidden. If nil then `defaultInlineRowHideOptions` are used - public var inlineRowHideOptions : InlineRowHideOptions? - + public var inlineRowHideOptions: InlineRowHideOptions? + /// Which `UIReturnKeyType` should be used by default. Applies only when `keyboardReturnType` is nil. public static var defaultKeyboardReturnType = KeyboardReturnTypeConfiguration() - + /// Which `UIReturnKeyType` should be used in this form. If nil then `defaultKeyboardReturnType` is used - public var keyboardReturnType : KeyboardReturnTypeConfiguration? - + public var keyboardReturnType: KeyboardReturnTypeConfiguration? + /// This form's delegate public weak var delegate: FormDelegate? - - public init(){} - + + public init() {} + /** Returns the row at the given indexPath */ public subscript(indexPath: IndexPath) -> BaseRow { return self[indexPath.section][indexPath.row] } - + /** Returns the row whose tag is passed as parameter. Uses a dictionary to get the row faster */ - public func rowBy(tag: String) -> RowOf? { + public func rowBy(tag: String) -> RowOf? where T: Equatable{ let row: BaseRow? = rowBy(tag: tag) return row as? RowOf } - + /** Returns the row whose tag is passed as parameter. Uses a dictionary to get the row faster */ - public func rowBy(tag: String) -> Row? { + public func rowBy(tag: String) -> Row? where Row: RowType{ let row: BaseRow? = rowBy(tag: tag) return row as? Row } - + /** Returns the row whose tag is passed as parameter. Uses a dictionary to get the row faster */ public func rowBy(tag: String) -> BaseRow? { return rowsByTag[tag] } - + /** Returns the section whose tag is passed as parameter. */ public func sectionBy(tag: String) -> Section? { - return kvoWrapper._allSections.filter( { $0.tag == tag }).first + return kvoWrapper._allSections.filter({ $0.tag == tag }).first } - + /** Method used to get all the values of all the rows of the form. Only rows with tag are included. - + - parameter includeHidden: If the values of hidden rows should be included. - + - returns: A dictionary mapping the rows tag to its value. [tag: value] */ - public func values(includeHidden: Bool = false) -> [String: Any?]{ + public func values(includeHidden: Bool = false) -> [String: Any?] { if includeHidden { - return allRows.filter({ $0.tag != nil }) - .reduce([String: Any?]()) { - var result = $0 - result[$1.tag!] = $1.baseValue - return result - } - } - return rows.filter({ $0.tag != nil }) - .reduce([String: Any?]()) { - var result = $0 - result[$1.tag!] = $1.baseValue - return result + return getValues(for: allRows.filter({ $0.tag != nil })) + .merging(getValues(for: allSections.filter({ $0 is MultivaluedSection && $0.tag != nil }) as? [MultivaluedSection]), uniquingKeysWith: {(_, new) in new }) } + return getValues(for: rows.filter({ $0.tag != nil })) + .merging(getValues(for: allSections.filter({ $0 is MultivaluedSection && $0.tag != nil }) as? [MultivaluedSection]), uniquingKeysWith: {(_, new) in new }) } - + /** Set values to the rows of this form - + - parameter values: A dictionary mapping tag to value of the rows to be set. [tag: value] */ - public func setValues(_ values: [String: Any?]){ - for (key, value) in values{ + public func setValues(_ values: [String: Any?]) { + for (key, value) in values { let row: BaseRow? = rowBy(tag: key) row?.baseValue = value } } - + /// The visible rows of this form public var rows: [BaseRow] { return flatMap { $0 } } - + /// All the rows of this form. Includes the hidden rows. public var allRows: [BaseRow] { return kvoWrapper._allSections.map({ $0.kvoWrapper._allRows }).flatMap { $0 } } - + /// All the sections of this form. Includes hidden sections. public var allSections: [Section] { return kvoWrapper._allSections } - + /** * Hides all the inline rows of this form. */ @@ -155,13 +144,13 @@ public final class Form { } } } - - //MARK: Private - + + // MARK: Private + var rowObservers = [String: [ConditionType: [Taggable]]]() var rowsByTag = [String: BaseRow]() var tagToValues = [String: Any]() - lazy var kvoWrapper : KVOWrapper = { [unowned self] in return KVOWrapper(form: self) }() + lazy var kvoWrapper: KVOWrapper = { [unowned self] in return KVOWrapper(form: self) }() } extension Form: Collection { @@ -170,12 +159,29 @@ extension Form: Collection { } extension Form: MutableCollection { - + // MARK: MutableCollectionType - + public subscript (_ position: Int) -> Section { get { return kvoWrapper.sections[position] as! Section } - set { kvoWrapper.sections[position] = newValue } + set { + if position > kvoWrapper.sections.count { + assertionFailure("Form: Index out of bounds") + } + + if position < kvoWrapper.sections.count { + let oldSection = kvoWrapper.sections[position] + let oldSectionIndex = kvoWrapper._allSections.index(of: oldSection as! Section)! + // Remove the previous section from the form + kvoWrapper._allSections[oldSectionIndex].willBeRemovedFromForm() + kvoWrapper._allSections[oldSectionIndex] = newValue + } else { + kvoWrapper._allSections.append(newValue) + } + + kvoWrapper.sections[position] = newValue + newValue.wasAddedTo(form: self) + } } public func index(after i: Int) -> Int { return i+1 <= endIndex ? i+1 : endIndex @@ -186,84 +192,88 @@ extension Form: MutableCollection { public var last: Section? { return reversed().first } - - + } extension Form : RangeReplaceableCollection { - + // MARK: RangeReplaceableCollectionType - - public func append(_ formSection: Section){ + + public func append(_ formSection: Section) { kvoWrapper.sections.insert(formSection, at: kvoWrapper.sections.count) kvoWrapper._allSections.append(formSection) formSection.wasAddedTo(form: self) } - - public func append(contentsOf newElements: S) where S.Iterator.Element == Section { + + public func append(contentsOf newElements: S) where S.Iterator.Element == Section { kvoWrapper.sections.addObjects(from: newElements.map { $0 }) kvoWrapper._allSections.append(contentsOf: newElements) - for section in newElements{ + for section in newElements { section.wasAddedTo(form: self) } } - - public func replaceSubrange(_ subRange: Range, with newElements: C) where C.Iterator.Element == Section { + + public func replaceSubrange(_ subRange: Range, with newElements: C) where C.Iterator.Element == Section { for i in subRange.lowerBound.. Int { guard index != 0 else { return 0 } - + let row = kvoWrapper.sections[index-1] - if let i = kvoWrapper._allSections.index(of: row as! Section){ + if let i = kvoWrapper._allSections.index(of: row as! Section) { return i + 1 } return kvoWrapper._allSections.count } - + } extension Form { - + // MARK: Private Helpers - - class KVOWrapper : NSObject { - dynamic var _sections = NSMutableArray() - var sections : NSMutableArray { return mutableArrayValue(forKey: "_sections") } + + class KVOWrapper: NSObject { + @objc dynamic private var _sections = NSMutableArray() + var sections: NSMutableArray { return mutableArrayValue(forKey: "_sections") } var _allSections = [Section]() - weak var form: Form? - - init(form: Form){ + private weak var form: Form? + + init(form: Form) { self.form = form super.init() addObserver(self, forKeyPath: "_sections", options: NSKeyValueObservingOptions.new.union(.old), context:nil) } - - deinit { removeObserver(self, forKeyPath: "_sections") } - + + deinit { + removeObserver(self, forKeyPath: "_sections") + _sections.removeAllObjects() + _allSections.removeAll() + } + public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { - + let newSections = change?[NSKeyValueChangeKey.newKey] as? [Section] ?? [] let oldSections = change?[NSKeyValueChangeKey.oldKey] as? [Section] ?? [] guard let delegateValue = form?.delegate, let keyPathValue = keyPath, let changeType = change?[NSKeyValueChangeKey.kindKey] else { return } @@ -286,67 +296,87 @@ extension Form { } } } - + func dictionaryValuesToEvaluatePredicate() -> [String: Any] { return tagToValues } - + func addRowObservers(to taggable: Taggable, rowTags: [String], type: ConditionType) { - for rowTag in rowTags{ + for rowTag in rowTags { if rowObservers[rowTag] == nil { rowObservers[rowTag] = Dictionary() } - if let _ = rowObservers[rowTag]?[type]{ - if !rowObservers[rowTag]![type]!.contains(where: { $0 === taggable }){ + if let _ = rowObservers[rowTag]?[type] { + if !rowObservers[rowTag]![type]!.contains(where: { $0 === taggable }) { rowObservers[rowTag]?[type]!.append(taggable) } - } - else{ + } else { rowObservers[rowTag]?[type] = [taggable] } } } - + func removeRowObservers(from taggable: Taggable, rowTags: [String], type: ConditionType) { - for rowTag in rowTags{ + for rowTag in rowTags { guard var arr = rowObservers[rowTag]?[type], let index = arr.index(where: { $0 === taggable }) else { continue } arr.remove(at: index) } } - + func nextRow(for row: BaseRow) -> BaseRow? { let allRows = rows guard let index = allRows.index(of: row) else { return nil } guard index < allRows.count - 1 else { return nil } return allRows[index + 1] } - + func previousRow(for row: BaseRow) -> BaseRow? { let allRows = rows guard let index = allRows.index(of: row) else { return nil } guard index > 0 else { return nil } return allRows[index - 1] } - - func hideSection(_ section: Section){ + + func hideSection(_ section: Section) { kvoWrapper.sections.remove(section) } - - func showSection(_ section: Section){ + + func showSection(_ section: Section) { guard !kvoWrapper.sections.contains(section) else { return } guard var index = kvoWrapper._allSections.index(of: section) else { return } var formIndex = NSNotFound - while (formIndex == NSNotFound && index > 0){ + while formIndex == NSNotFound && index > 0 { index = index - 1 let previous = kvoWrapper._allSections[index] formIndex = kvoWrapper.sections.index(of: previous) } kvoWrapper.sections.insert(section, at: formIndex == NSNotFound ? 0 : formIndex + 1 ) } + + var containsMultivaluedSection: Bool { + return kvoWrapper._allSections.contains { $0 is MultivaluedSection } + } + + func getValues(for rows: [BaseRow]) -> [String: Any?] { + return rows.reduce([String: Any?]()) { + var result = $0 + result[$1.tag!] = $1.baseValue + return result + } + } + + func getValues(for multivaluedSections: [MultivaluedSection]?) -> [String: [Any?]] { + return multivaluedSections?.reduce([String: [Any?]]()) { + var result = $0 + result[$1.tag!] = $1.values() + return result + } ?? [:] + } + } extension Form { - + @discardableResult public func validate(includeHidden: Bool = false) -> [ValidationError] { let rowsToValidate = includeHidden ? allRows : rows @@ -356,4 +386,10 @@ extension Form { return res } } + + // Reset rows validation + public func cleanValidationErrors(){ + allRows.forEach { $0.cleanValidationErrors() } + } } + diff --git a/Pods/Eureka/Source/Core/HeaderFooterView.swift b/Pods/Eureka/Source/Core/HeaderFooterView.swift index 2534c73..5a30c67 100644 --- a/Pods/Eureka/Source/Core/HeaderFooterView.swift +++ b/Pods/Eureka/Source/Core/HeaderFooterView.swift @@ -22,7 +22,6 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. - import Foundation /** @@ -33,22 +32,22 @@ import Foundation - NibFile: Will load the view from a nib file. */ public enum HeaderFooterProvider { - + /** * Will generate a view of the specified class. */ case `class` - + /** * Will generate the view as a result of the given closure. */ case callback(()->ViewType) - + /** * Will load the view from a nib file. */ case nibFile(name: String, bundle: Bundle?) - + internal func createView() -> ViewType { switch self { case .class: @@ -72,20 +71,19 @@ public enum HeaderFooterType { * Struct used to generate headers and footers either from a view or a String. */ public struct HeaderFooterView : ExpressibleByStringLiteral, HeaderFooterViewRepresentable { - + /// Holds the title of the view if it was set up with a String. public var title: String? - + /// Generates the view. public var viewProvider: HeaderFooterProvider? - + /// Closure called when the view is created. Useful to customize its appearance. - public var onSetupView: ((_ view: ViewType, _ section: Section) -> ())? - + public var onSetupView: ((_ view: ViewType, _ section: Section) -> Void)? + /// A closure that returns the height for the header or footer view. - public var height: (()->CGFloat)? - - + public var height: (() -> CGFloat)? + /** This method can be called to get the view corresponding to the header or footer of a section in a specific controller. @@ -103,8 +101,7 @@ public struct HeaderFooterView : ExpressibleByStringLiteral, H section.headerView = result return result }() - } - else { + } else { view = section.footerView as? ViewType ?? { let result = viewProvider?.createView() section.footerView = result @@ -115,36 +112,36 @@ public struct HeaderFooterView : ExpressibleByStringLiteral, H onSetupView?(v, section) return v } - + /** Initiates the view with a String as title */ - public init?(title: String?){ + public init?(title: String?) { guard let t = title else { return nil } self.init(stringLiteral: t) } - + /** Initiates the view with a view provider, ideal for customized headers or footers */ - public init(_ provider: HeaderFooterProvider){ + public init(_ provider: HeaderFooterProvider) { viewProvider = provider } - + /** Initiates the view with a String as title */ public init(unicodeScalarLiteral value: String) { self.title = value } - + /** Initiates the view with a String as title */ public init(extendedGraphemeClusterLiteral value: String) { self.title = value } - + /** Initiates the view with a String as title */ @@ -154,11 +151,11 @@ public struct HeaderFooterView : ExpressibleByStringLiteral, H } extension UIView { - + func eurekaInvalidate() { setNeedsUpdateConstraints() updateConstraintsIfNeeded() setNeedsLayout() } - + } diff --git a/Pods/Eureka/Source/Core/Helpers.swift b/Pods/Eureka/Source/Core/Helpers.swift index 2cb6f2e..1fac73e 100644 --- a/Pods/Eureka/Source/Core/Helpers.swift +++ b/Pods/Eureka/Source/Core/Helpers.swift @@ -26,7 +26,7 @@ import Foundation import UIKit extension UIView { - + public func findFirstResponder() -> UIView? { if isFirstResponder { return self } for subView in subviews { @@ -36,7 +36,7 @@ extension UIView { } return nil } - + public func formCell() -> BaseCell? { if self is UITableViewCell { return self as? BaseCell @@ -46,15 +46,14 @@ extension UIView { } extension NSPredicate { - + var predicateVars: [String] { var ret = [String]() if let compoundPredicate = self as? NSCompoundPredicate { for subPredicate in compoundPredicate.subpredicates where subPredicate is NSPredicate { ret.append(contentsOf: (subPredicate as! NSPredicate).predicateVars) } - } - else if let comparisonPredicate = self as? NSComparisonPredicate { + } else if let comparisonPredicate = self as? NSComparisonPredicate { ret.append(contentsOf: comparisonPredicate.leftExpression.expressionVars) ret.append(contentsOf: comparisonPredicate.rightExpression.expressionVars) } @@ -63,16 +62,15 @@ extension NSPredicate { } extension NSExpression { - + var expressionVars: [String] { - switch expressionType{ + switch expressionType { case .function, .variable: let str = "\(self)" - if let range = str.range(of: "."){ - return [str.substring(with: str.characters.index(str.startIndex, offsetBy: 1)..()) -> Self { + public func onExpandInlineRow(_ callback: @escaping (Cell, Self, InlineRow) -> Void) -> Self { callbackOnExpandInlineRow = callback return self } - + /** Sets a block to be executed when a row is collapsed. */ @discardableResult - public func onCollapseInlineRow(_ callback: @escaping (Cell, Self, InlineRow)->()) -> Self { + public func onCollapseInlineRow(_ callback: @escaping (Cell, Self, InlineRow) -> Void) -> Self { callbackOnCollapseInlineRow = callback return self } - + /// Returns the block that will be executed when this row expands - public var onCollapseInlineRowCallback: ((Cell, Self, InlineRow)->())? { - return callbackOnCollapseInlineRow as! ((Cell, Self, InlineRow)->())? + public var onCollapseInlineRowCallback: ((Cell, Self, InlineRow) -> Void)? { + return callbackOnCollapseInlineRow as! ((Cell, Self, InlineRow) -> Void)? } - + /// Returns the block that will be executed when this row collapses - public var onExpandInlineRowCallback: ((Cell, Self, InlineRow)->())? { - return callbackOnExpandInlineRow as! ((Cell, Self, InlineRow)->())? + public var onExpandInlineRowCallback: ((Cell, Self, InlineRow) -> Void)? { + return callbackOnExpandInlineRow as! ((Cell, Self, InlineRow) -> Void)? } - + public var isExpanded: Bool { return _inlineRow != nil } public var isCollapsed: Bool { return !isExpanded } } diff --git a/Pods/Eureka/Source/Core/NavigationAccessoryView.swift b/Pods/Eureka/Source/Core/NavigationAccessoryView.swift index 8021e56..0d3de52 100644 --- a/Pods/Eureka/Source/Core/NavigationAccessoryView.swift +++ b/Pods/Eureka/Source/Core/NavigationAccessoryView.swift @@ -22,17 +22,16 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. - import Foundation /// Class for the navigation accessory view used in FormViewController -open class NavigationAccessoryView : UIToolbar { +open class NavigationAccessoryView: UIToolbar { open var previousButton: UIBarButtonItem! open var nextButton: UIBarButtonItem! open var doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: nil, action: nil) private var fixedSpace = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil) private var flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) - + public override init(frame: CGRect) { super.init(frame: CGRect(x: 0, y: 0, width: frame.size.width, height: 44.0)) autoresizingMask = .flexibleWidth @@ -40,7 +39,7 @@ open class NavigationAccessoryView : UIToolbar { initializeChevrons() setItems([previousButton, fixedSpace, nextButton, flexibleSpace, doneButton], animated: false) } - + required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } @@ -64,6 +63,6 @@ open class NavigationAccessoryView : UIToolbar { previousButton = UIBarButtonItem(image: imageLeftChevron, style: .plain, target: nil, action: nil) nextButton = UIBarButtonItem(image: imageRightChevron, style: .plain, target: nil, action: nil) } - + open override func touchesBegan(_ touches: Set, with event: UIEvent?) {} } diff --git a/Pods/Eureka/Source/Core/Operators.swift b/Pods/Eureka/Source/Core/Operators.swift index ab84021..a98a23d 100644 --- a/Pods/Eureka/Source/Core/Operators.swift +++ b/Pods/Eureka/Source/Core/Operators.swift @@ -47,7 +47,7 @@ infix operator +++ : FormPrecedence - returns: the updated form */ @discardableResult -public func +++(left: Form, right: Section) -> Form { +public func +++ (left: Form, right: Section) -> Form { left.append(right) return left } @@ -59,7 +59,7 @@ public func +++(left: Form, right: Section) -> Form { - parameter right: the row */ @discardableResult -public func +++(left: Form, right: BaseRow) -> Form { +public func +++ (left: Form, right: BaseRow) -> Form { let section = Section() let _ = left +++ section <<< right return left @@ -74,7 +74,7 @@ public func +++(left: Form, right: BaseRow) -> Form { - returns: the created form */ @discardableResult -public func +++(left: Section, right: Section) -> Form { +public func +++ (left: Section, right: Section) -> Form { let form = Form() let _ = form +++ left +++ right return form @@ -89,7 +89,7 @@ public func +++(left: Section, right: Section) -> Form { - returns: the form */ @discardableResult -public func +++(left: Section, right: BaseRow) -> Form { +public func +++ (left: Section, right: BaseRow) -> Form { let section = Section() section <<< right return left +++ section @@ -104,7 +104,7 @@ public func +++(left: Section, right: BaseRow) -> Form { - returns: the created form */ @discardableResult -public func +++(left: BaseRow, right: BaseRow) -> Form { +public func +++ (left: BaseRow, right: BaseRow) -> Form { let form = Section() <<< left +++ Section() <<< right return form } @@ -120,7 +120,7 @@ infix operator <<< : SectionPrecedence - returns: the section */ @discardableResult -public func <<<(left: Section, right: BaseRow) -> Section { +public func <<< (left: Section, right: BaseRow) -> Section { left.append(right) return left } @@ -134,7 +134,7 @@ public func <<<(left: Section, right: BaseRow) -> Section { - returns: the created section */ @discardableResult -public func <<<(left: BaseRow, right: BaseRow) -> Section { +public func <<< (left: BaseRow, right: BaseRow) -> Section { let section = Section() section <<< left <<< right return section @@ -146,7 +146,7 @@ public func <<<(left: BaseRow, right: BaseRow) -> Section { - parameter lhs: the section - parameter rhs: the rows to be appended */ -public func += (lhs: inout Section, rhs: C) where C.Iterator.Element == BaseRow{ +public func += (lhs: inout Section, rhs: C) where C.Iterator.Element == BaseRow { lhs.append(contentsOf: rhs) } @@ -156,6 +156,6 @@ public func += (lhs: inout Section, rhs: C) where C.Iterator.Ele - parameter lhs: the form - parameter rhs: the sections to be appended */ -public func += (lhs: inout Form, rhs: C) where C.Iterator.Element == Section{ +public func += (lhs: inout Form, rhs: C) where C.Iterator.Element == Section { lhs.append(contentsOf: rhs) } diff --git a/Pods/Eureka/Source/Core/PresenterRowType.swift b/Pods/Eureka/Source/Core/PresenterRowType.swift index 77ad026..edeebc5 100644 --- a/Pods/Eureka/Source/Core/PresenterRowType.swift +++ b/Pods/Eureka/Source/Core/PresenterRowType.swift @@ -22,7 +22,6 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. - import Foundation /** @@ -30,18 +29,18 @@ import Foundation * This includes presenting or pushing view controllers. */ public protocol PresenterRowType: TypedRowType { - - associatedtype ProviderType : UIViewController, TypedRowControllerType - + + associatedtype PresentedControllerType : UIViewController, TypedRowControllerType + /// Defines how the view controller will be presented, pushed, etc. - var presentationMode: PresentationMode? { get set } - + var presentationMode: PresentationMode? { get set } + /// Will be called before the presentation occurs. - var onPresentCallback: ((FormViewController, ProviderType)->())? { get set } + var onPresentCallback: ((FormViewController, PresentedControllerType) -> Void)? { get set } } extension PresenterRowType { - + /** Sets a block to be executed when the row presents a view controller @@ -49,7 +48,7 @@ extension PresenterRowType { - returns: this row */ - public func onPresent(_ callback: ((FormViewController, ProviderType)->())?) -> Self { + public func onPresent(_ callback: ((FormViewController, PresentedControllerType) -> Void)?) -> Self { onPresentCallback = callback return self } diff --git a/Pods/Eureka/Source/Core/Row.swift b/Pods/Eureka/Source/Core/Row.swift index 62229fe..542cb09 100644 --- a/Pods/Eureka/Source/Core/Row.swift +++ b/Pods/Eureka/Source/Core/Row.swift @@ -22,10 +22,9 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. - import Foundation -open class RowOf: BaseRow { +open class RowOf: BaseRow where T: Equatable{ private var _value: T? { didSet { @@ -37,12 +36,12 @@ open class RowOf: BaseRow { } guard let t = tag else { return } form.tagToValues[t] = (value != nil ? value! : NSNull()) - if let rowObservers = form.rowObservers[t]?[.hidden]{ + if let rowObservers = form.rowObservers[t]?[.hidden] { for rowObserver in rowObservers { (rowObserver as? Hidable)?.evaluateHidden() } } - if let rowObservers = form.rowObservers[t]?[.disabled]{ + if let rowObservers = form.rowObservers[t]?[.disabled] { for rowObserver in rowObservers { (rowObserver as? Disableable)?.evaluateDisabled() } @@ -51,12 +50,12 @@ open class RowOf: BaseRow { } /// The typed value of this row. - open var value : T?{ - set (newValue){ + open var value: T? { + set (newValue) { _value = newValue guard let _ = section?.form else { return } wasChanged = true - if validationOptions.contains(.validatesOnChange) || (wasBlurred && validationOptions.contains(.validatesOnChangeAfterBlurred)) || !isValid { + if validationOptions.contains(.validatesOnChange) || (wasBlurred && validationOptions.contains(.validatesOnChangeAfterBlurred)) || (!isValid && validationOptions != .validatesOnDemand) { validate() } } @@ -71,15 +70,12 @@ open class RowOf: BaseRow { set { value = newValue as? T } } - /// Variable used in rows with options that serves to generate the options for that row. - public var dataProvider: DataProvider? - /// Block variable used to get the String that should be displayed for the value of this row. - public var displayValueFor : ((T?) -> String?)? = { + public var displayValueFor: ((T?) -> String?)? = { return $0.map { String(describing: $0) } } - public required init(tag: String?){ + public required init(tag: String?) { super.init(tag: tag) } @@ -87,25 +83,33 @@ open class RowOf: BaseRow { @discardableResult public override func validate() -> [ValidationError] { + #if swift(>=4.1) + validationErrors = rules.compactMap { $0.validateFn(value) } + #else validationErrors = rules.flatMap { $0.validateFn(value) } + #endif return validationErrors } - public func add(rule: Rule) where T == Rule.RowValueType{ + /// Add a Validation rule for the Row + /// - Parameter rule: RuleType object to add + public func add(rule: Rule) where T == Rule.RowValueType { let validFn: ((T?) -> ValidationError?) = { (val: T?) in return rule.isValid(value: val) } rules.append(ValidationRuleHelper(validateFn: validFn, rule: rule)) } - public func add(ruleSet: RuleSet){ + /// Add a Validation rule set for the Row + /// - Parameter ruleSet: RuleSet set of rules to add + public func add(ruleSet: RuleSet) { rules.append(contentsOf: ruleSet.rules) } public func remove(ruleWithIdentifier identifier: String) { if let index = rules.index(where: { (validationRuleHelper) -> Bool in return validationRuleHelper.rule.id == identifier - }){ + }) { rules.remove(at: index) } } @@ -129,12 +133,12 @@ open class Row: RowOf, TypedRowType where Cell: Base private var _cell: Cell! { didSet { RowDefaults.cellSetup["\(type(of: self))"]?(_cell, self) - (callbackCellSetup as? ((Cell) -> ()))?(_cell) + (callbackCellSetup as? ((Cell) -> Void))?(_cell) } } /// The cell associated to this row. - public var cell : Cell! { + public var cell: Cell! { return _cell ?? { let result = cellProvider.makeCell(style: self.cellStyle) result.row = self @@ -177,11 +181,11 @@ open class Row: RowOf, TypedRowType where Cell: Base /** Will be called inside `didSelect` method of the row. Can be used to customize row selection from the definition of the row. */ - open func customDidSelect(){} + open func customDidSelect() {} /** Will be called inside `updateCell` method of the row. Can be used to customize reloading a row from its definition. */ - open func customUpdateCell(){} + open func customUpdateCell() {} } diff --git a/Pods/Eureka/Source/Core/RowControllerType.swift b/Pods/Eureka/Source/Core/RowControllerType.swift index 15243af..96123b3 100644 --- a/Pods/Eureka/Source/Core/RowControllerType.swift +++ b/Pods/Eureka/Source/Core/RowControllerType.swift @@ -22,15 +22,13 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. - import Foundation - /** * Base protocol for view controllers presented by Eureka rows. */ -public protocol RowControllerType : NSObjectProtocol { - +public protocol RowControllerType: NSObjectProtocol { + /// A closure to be called when the controller disappears. - var onDismissCallback: ((UIViewController) -> ())? { get set } + var onDismissCallback: ((UIViewController) -> Void)? { get set } } diff --git a/Pods/Eureka/Source/Core/RowProtocols.swift b/Pods/Eureka/Source/Core/RowProtocols.swift index 6d9eb80..1dce667 100644 --- a/Pods/Eureka/Source/Core/RowProtocols.swift +++ b/Pods/Eureka/Source/Core/RowProtocols.swift @@ -22,30 +22,26 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. - import Foundation - /** * Protocol that view controllers pushed or presented by a row should conform to. */ -public protocol TypedRowControllerType : RowControllerType { +public protocol TypedRowControllerType: RowControllerType { associatedtype RowValue: Equatable - + /// The row that pushed or presented this controller - var row : RowOf! { get set } + var row: RowOf! { get set } } - - -//MARK: Header Footer Protocols +// MARK: Header Footer Protocols /** * Protocol used to set headers and footers to sections. * Can be set with a view or a String */ public protocol HeaderFooterViewRepresentable { - + /** This method can be called to get the view corresponding to the header or footer of a section in a specific controller. @@ -56,12 +52,10 @@ public protocol HeaderFooterViewRepresentable { - returns: The header or footer of the specified section. */ func viewForSection(_ section: Section, type: HeaderFooterType) -> UIView? - + /// If the header or footer of a section was created with a String then it will be stored in the title. var title: String? { get set } - + /// The height of the header or footer. - var height: (()->CGFloat)? { get set } + var height: (() -> CGFloat)? { get set } } - - diff --git a/Pods/Eureka/Source/Core/RowType.swift b/Pods/Eureka/Source/Core/RowType.swift index 320719f..b076bba 100644 --- a/Pods/Eureka/Source/Core/RowType.swift +++ b/Pods/Eureka/Source/Core/RowType.swift @@ -22,26 +22,25 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. - import Foundation -protocol Disableable : Taggable { +protocol Disableable: Taggable { func evaluateDisabled() - var disabled : Condition? { get set } - var isDisabled : Bool { get } + var disabled: Condition? { get set } + var isDisabled: Bool { get } } protocol Hidable: Taggable { func evaluateHidden() - var hidden : Condition? { get set } - var isHidden : Bool { get } + var hidden: Condition? { get set } + var isHidden: Bool { get } } -public protocol KeyboardReturnHandler : BaseRowType { - var keyboardReturnType : KeyboardReturnTypeConfiguration? { get set } +public protocol KeyboardReturnHandler: BaseRowType { + var keyboardReturnType: KeyboardReturnTypeConfiguration? { get set } } -public protocol Taggable : AnyObject { +public protocol Taggable: AnyObject { var tag: String? { get set } } @@ -54,7 +53,7 @@ public protocol BaseRowType: Taggable { var section: Section? { get } /// Parameter used when creating the cell for this row. - var cellStyle : UITableViewCellStyle { get set } + var cellStyle: UITableViewCellStyle { get set } /// The title will be displayed in the textLabel of the row. var title: String? { get set } @@ -68,7 +67,7 @@ public protocol BaseRowType: Taggable { Method called when the cell belonging to this row was selected. Must call the corresponding method in its cell. */ func didSelect() - + /** Typically we don't need to explicitly call this method since it is called by Eureka framework. It will validates the row if you invoke it. */ @@ -80,10 +79,10 @@ public protocol TypedRowType: BaseRowType { associatedtype Cell: BaseCell, TypedCellType /// The typed cell associated to this row. - var cell : Cell! { get } + var cell: Cell! { get } /// The typed value this row stores. - var value : Cell.Value? { get set } + var value: Cell.Value? { get set } func add(rule: Rule) where Rule.RowValueType == Cell.Value func remove(ruleWithIdentifier: String) @@ -93,7 +92,7 @@ public protocol TypedRowType: BaseRowType { * Protocol that every row type has to conform to. */ public protocol RowType: TypedRowType { - init(_ tag: String?, _ initializer: (Self) -> ()) + init(_ tag: String?, _ initializer: (Self) -> Void) } extension RowType where Self: BaseRow { @@ -101,104 +100,98 @@ extension RowType where Self: BaseRow { /** Default initializer for a row */ - public init(_ tag: String? = nil, _ initializer: (Self) -> () = { _ in }) { + public init(_ tag: String? = nil, _ initializer: (Self) -> Void = { _ in }) { self.init(tag: tag) RowDefaults.rowInitialization["\(type(of: self))"]?(self) initializer(self) } } - extension RowType where Self: BaseRow { /// The default block executed when the cell is updated. Applies to every row of this type. - public static var defaultCellUpdate:((Cell, Self) -> ())? { + public static var defaultCellUpdate: ((Cell, Self) -> Void)? { set { if let newValue = newValue { - let wrapper : (BaseCell, BaseRow) -> Void = { (baseCell: BaseCell, baseRow: BaseRow) in + let wrapper: (BaseCell, BaseRow) -> Void = { (baseCell: BaseCell, baseRow: BaseRow) in newValue(baseCell as! Cell, baseRow as! Self) } RowDefaults.cellUpdate["\(self)"] = wrapper RowDefaults.rawCellUpdate["\(self)"] = newValue - } - else { + } else { RowDefaults.cellUpdate["\(self)"] = nil RowDefaults.rawCellUpdate["\(self)"] = nil } } - get{ return RowDefaults.rawCellUpdate["\(self)"] as? ((Cell, Self) -> ()) } + get { return RowDefaults.rawCellUpdate["\(self)"] as? ((Cell, Self) -> Void) } } /// The default block executed when the cell is created. Applies to every row of this type. - public static var defaultCellSetup:((Cell, Self) -> ())? { + public static var defaultCellSetup: ((Cell, Self) -> Void)? { set { if let newValue = newValue { - let wrapper : (BaseCell, BaseRow) -> Void = { (baseCell: BaseCell, baseRow: BaseRow) in + let wrapper: (BaseCell, BaseRow) -> Void = { (baseCell: BaseCell, baseRow: BaseRow) in newValue(baseCell as! Cell, baseRow as! Self) } RowDefaults.cellSetup["\(self)"] = wrapper RowDefaults.rawCellSetup["\(self)"] = newValue - } - else { + } else { RowDefaults.cellSetup["\(self)"] = nil RowDefaults.rawCellSetup["\(self)"] = nil } } - get{ return RowDefaults.rawCellSetup["\(self)"] as? ((Cell, Self) -> ()) } + get { return RowDefaults.rawCellSetup["\(self)"] as? ((Cell, Self) -> Void) } } /// The default block executed when the cell becomes first responder. Applies to every row of this type. - public static var defaultOnCellHighlightChanged:((Cell, Self) -> ())? { + public static var defaultOnCellHighlightChanged: ((Cell, Self) -> Void)? { set { if let newValue = newValue { - let wrapper : (BaseCell, BaseRow) -> Void = { (baseCell: BaseCell, baseRow: BaseRow) in + let wrapper: (BaseCell, BaseRow) -> Void = { (baseCell: BaseCell, baseRow: BaseRow) in newValue(baseCell as! Cell, baseRow as! Self) } RowDefaults.onCellHighlightChanged ["\(self)"] = wrapper RowDefaults.rawOnCellHighlightChanged["\(self)"] = newValue - } - else { + } else { RowDefaults.onCellHighlightChanged["\(self)"] = nil RowDefaults.rawOnCellHighlightChanged["\(self)"] = nil } } - get{ return RowDefaults.rawOnCellHighlightChanged["\(self)"] as? ((Cell, Self) -> ()) } + get { return RowDefaults.rawOnCellHighlightChanged["\(self)"] as? ((Cell, Self) -> Void) } } /// The default block executed to initialize a row. Applies to every row of this type. - public static var defaultRowInitializer:((Self) -> ())? { + public static var defaultRowInitializer: ((Self) -> Void)? { set { if let newValue = newValue { - let wrapper : (BaseRow) -> Void = { (baseRow: BaseRow) in + let wrapper: (BaseRow) -> Void = { (baseRow: BaseRow) in newValue(baseRow as! Self) } RowDefaults.rowInitialization["\(self)"] = wrapper RowDefaults.rawRowInitialization["\(self)"] = newValue - } - else { + } else { RowDefaults.rowInitialization["\(self)"] = nil RowDefaults.rawRowInitialization["\(self)"] = nil } } - get { return RowDefaults.rawRowInitialization["\(self)"] as? ((Self) -> ()) } + get { return RowDefaults.rawRowInitialization["\(self)"] as? ((Self) -> Void) } } /// The default block executed to initialize a row. Applies to every row of this type. - public static var defaultOnRowValidationChanged:((Cell, Self) -> ())? { + public static var defaultOnRowValidationChanged: ((Cell, Self) -> Void)? { set { if let newValue = newValue { - let wrapper : (BaseCell, BaseRow) -> Void = { (baseCell: BaseCell, baseRow: BaseRow) in + let wrapper: (BaseCell, BaseRow) -> Void = { (baseCell: BaseCell, baseRow: BaseRow) in newValue(baseCell as! Cell, baseRow as! Self) } RowDefaults.onRowValidationChanged["\(self)"] = wrapper RowDefaults.rawOnRowValidationChanged["\(self)"] = newValue - } - else { + } else { RowDefaults.onRowValidationChanged["\(self)"] = nil RowDefaults.rawOnRowValidationChanged["\(self)"] = nil } } - get{ return RowDefaults.rawOnRowValidationChanged["\(self)"] as? ((Cell, Self) -> ()) } + get { return RowDefaults.rawOnRowValidationChanged["\(self)"] as? ((Cell, Self) -> Void) } } /** @@ -207,7 +200,7 @@ extension RowType where Self: BaseRow { - returns: this row */ @discardableResult - public func onChange(_ callback: @escaping (Self) -> ()) -> Self{ + public func onChange(_ callback: @escaping (Self) -> Void) -> Self { callbackOnChange = { [unowned self] in callback(self) } return self } @@ -218,7 +211,7 @@ extension RowType where Self: BaseRow { - returns: this row */ @discardableResult - public func cellUpdate(_ callback: @escaping ((_ cell: Cell, _ row: Self) -> ())) -> Self{ + public func cellUpdate(_ callback: @escaping ((_ cell: Cell, _ row: Self) -> Void)) -> Self { callbackCellUpdate = { [unowned self] in callback(self.cell, self) } return self } @@ -229,7 +222,7 @@ extension RowType where Self: BaseRow { - returns: this row */ @discardableResult - public func cellSetup(_ callback: @escaping ((_ cell: Cell, _ row: Self) -> ())) -> Self{ + public func cellSetup(_ callback: @escaping ((_ cell: Cell, _ row: Self) -> Void)) -> Self { callbackCellSetup = { [unowned self] (cell: Cell) in callback(cell, self) } return self } @@ -240,7 +233,7 @@ extension RowType where Self: BaseRow { - returns: this row */ @discardableResult - public func onCellSelection(_ callback: @escaping ((_ cell: Cell, _ row: Self) -> ())) -> Self{ + public func onCellSelection(_ callback: @escaping ((_ cell: Cell, _ row: Self) -> Void)) -> Self { callbackCellOnSelection = { [unowned self] in callback(self.cell, self) } return self } @@ -251,13 +244,13 @@ extension RowType where Self: BaseRow { - returns: this row */ @discardableResult - public func onCellHighlightChanged(_ callback: @escaping (_ cell: Cell, _ row: Self)->()) -> Self { + public func onCellHighlightChanged(_ callback: @escaping (_ cell: Cell, _ row: Self) -> Void) -> Self { callbackOnCellHighlightChanged = { [unowned self] in callback(self.cell, self) } return self } @discardableResult - public func onRowValidationChanged(_ callback: @escaping (_ cell: Cell, _ row: Self)->()) -> Self { + public func onRowValidationChanged(_ callback: @escaping (_ cell: Cell, _ row: Self) -> Void) -> Self { callbackOnRowValidationChanged = { [unowned self] in callback(self.cell, self) } return self } diff --git a/Pods/Eureka/Source/Core/Section.swift b/Pods/Eureka/Source/Core/Section.swift index a68df28..4747e13 100644 --- a/Pods/Eureka/Source/Core/Section.swift +++ b/Pods/Eureka/Source/Core/Section.swift @@ -22,64 +22,61 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. - import Foundation /// The delegate of the Eureka sections. public protocol SectionDelegate: class { func rowsHaveBeenAdded(_ rows: [BaseRow], at: IndexSet) func rowsHaveBeenRemoved(_ rows: [BaseRow], at: IndexSet) - func rowsHaveBeenReplaced(oldRows:[BaseRow], newRows: [BaseRow], at: IndexSet) + func rowsHaveBeenReplaced(oldRows: [BaseRow], newRows: [BaseRow], at: IndexSet) } // MARK: Section extension Section : Equatable {} -public func ==(lhs: Section, rhs: Section) -> Bool{ +public func == (lhs: Section, rhs: Section) -> Bool { return lhs === rhs } extension Section : Hidable, SectionDelegate {} extension Section { - + public func reload(with rowAnimation: UITableViewRowAnimation = .none) { - guard let tableView = (form?.delegate as? FormViewController)?.tableView, let index = index else { return } + guard let tableView = (form?.delegate as? FormViewController)?.tableView, let index = index, index < tableView.numberOfSections else { return } tableView.reloadSections(IndexSet(integer: index), with: rowAnimation) } } +extension Section { + internal class KVOWrapper: NSObject { -extension Section { - - internal class KVOWrapper : NSObject{ - - dynamic var _rows = NSMutableArray() - var rows : NSMutableArray { - get { - return mutableArrayValue(forKey: "_rows") - } + @objc dynamic private var _rows = NSMutableArray() + var rows: NSMutableArray { + return mutableArrayValue(forKey: "_rows") } var _allRows = [BaseRow]() - - weak var section: Section? - - init(section: Section){ + + private weak var section: Section? + + init(section: Section) { self.section = section super.init() addObserver(self, forKeyPath: "_rows", options: NSKeyValueObservingOptions.new.union(.old), context:nil) } - - deinit{ + + deinit { removeObserver(self, forKeyPath: "_rows") + _rows.removeAllObjects() + _allRows.removeAll() } - + public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { let newRows = change![NSKeyValueChangeKey.newKey] as? [BaseRow] ?? [] let oldRows = change![NSKeyValueChangeKey.oldKey] as? [BaseRow] ?? [] - guard let keyPathValue = keyPath, let changeType = change?[NSKeyValueChangeKey.kindKey] else{ return } + guard let keyPathValue = keyPath, let changeType = change?[NSKeyValueChangeKey.kindKey] else { return } let delegateValue = section?.form?.delegate guard keyPathValue == "_rows" else { return } switch (changeType as! NSNumber).uintValue { @@ -90,13 +87,13 @@ extension Section { let indexSet = change![NSKeyValueChangeKey.indexesKey] as! IndexSet section?.rowsHaveBeenAdded(newRows, at: indexSet) if let _index = section?.index { - delegateValue?.rowsHaveBeenAdded(newRows, at: indexSet.map { IndexPath(row: $0, section: _index ) } ) + delegateValue?.rowsHaveBeenAdded(newRows, at: indexSet.map { IndexPath(row: $0, section: _index ) }) } case NSKeyValueChange.removal.rawValue: let indexSet = change![NSKeyValueChangeKey.indexesKey] as! IndexSet section?.rowsHaveBeenRemoved(oldRows, at: indexSet) if let _index = section?.index { - delegateValue?.rowsHaveBeenRemoved(oldRows, at: indexSet.map { IndexPath(row: $0, section: _index ) } ) + delegateValue?.rowsHaveBeenRemoved(oldRows, at: indexSet.map { IndexPath(row: $0, section: _index ) }) } case NSKeyValueChange.replacement.rawValue: let indexSet = change![NSKeyValueChangeKey.indexesKey] as! IndexSet @@ -109,7 +106,7 @@ extension Section { } } } - + /** * If this section contains a row (hidden or not) with the passed parameter as tag then that row will be returned. * If not, it returns nil. @@ -120,162 +117,198 @@ extension Section { } } - /// The class representing the sections in a Eureka form. open class Section { - + /// The tag is used to uniquely identify a Section. Must be unique among sections and rows. public var tag: String? - + /// The form that contains this section public internal(set) weak var form: Form? - + /// The header of this section. public var header: HeaderFooterViewRepresentable? { willSet { headerView = nil } } - + /// The footer of this section public var footer: HeaderFooterViewRepresentable? { willSet { footerView = nil } } - + /// Index of this section in the form it belongs to. public var index: Int? { return form?.index(of: self) } - + /// Condition that determines if the section should be hidden or not. - public var hidden : Condition? { + public var hidden: Condition? { willSet { removeFromRowObservers() } didSet { addToRowObservers() } } - + /// Returns if the section is currently hidden or not - public var isHidden : Bool { return hiddenCache } - - public required init(){} - - public required init(_ initializer: (Section) -> ()){ + public var isHidden: Bool { return hiddenCache } + + public required init() {} + + #if swift(>=4.1) + public required init(_ elements: S) where S: Sequence, S.Element == BaseRow { + self.append(contentsOf: elements) + } + #endif + + public init(_ initializer: @escaping (Section) -> Void) { initializer(self) } - - public init(_ header: String, _ initializer: (Section) -> () = { _ in }){ + + public init(_ header: String, _ initializer: @escaping (Section) -> Void = { _ in }) { self.header = HeaderFooterView(stringLiteral: header) initializer(self) } - - public init(header: String, footer: String, _ initializer: (Section) -> () = { _ in }){ + + public init(header: String, footer: String, _ initializer: (Section) -> Void = { _ in }) { self.header = HeaderFooterView(stringLiteral: header) self.footer = HeaderFooterView(stringLiteral: footer) initializer(self) } - - public init(footer: String, _ initializer: (Section) -> () = { _ in }){ + + public init(footer: String, _ initializer: (Section) -> Void = { _ in }) { self.footer = HeaderFooterView(stringLiteral: footer) initializer(self) } - - //MARK: SectionDelegate - + + // MARK: SectionDelegate + /** * Delegate method called by the framework when one or more rows have been added to the section. */ open func rowsHaveBeenAdded(_ rows: [BaseRow], at: IndexSet) {} - + /** * Delegate method called by the framework when one or more rows have been removed from the section. */ open func rowsHaveBeenRemoved(_ rows: [BaseRow], at: IndexSet) {} - + /** * Delegate method called by the framework when one or more rows have been replaced in the section. */ - open func rowsHaveBeenReplaced(oldRows:[BaseRow], newRows: [BaseRow], at: IndexSet) {} - - //MARK: Private + open func rowsHaveBeenReplaced(oldRows: [BaseRow], newRows: [BaseRow], at: IndexSet) {} + + // MARK: Private lazy var kvoWrapper: KVOWrapper = { [unowned self] in return KVOWrapper(section: self) }() + var headerView: UIView? var footerView: UIView? var hiddenCache = false } +extension Section: MutableCollection, BidirectionalCollection { + + // MARK: MutableCollectionType -extension Section : MutableCollection, BidirectionalCollection { - - //MARK: MutableCollectionType - public var startIndex: Int { return 0 } public var endIndex: Int { return kvoWrapper.rows.count } public subscript (position: Int) -> BaseRow { get { - if position >= kvoWrapper.rows.count{ + if position >= kvoWrapper.rows.count { assertionFailure("Section: Index out of bounds") } return kvoWrapper.rows[position] as! BaseRow } - set { kvoWrapper.rows[position] = newValue } + set { + if position > kvoWrapper.rows.count { + assertionFailure("Section: Index out of bounds") + } + + if position < kvoWrapper.rows.count { + let oldRow = kvoWrapper.rows[position] + let oldRowIndex = kvoWrapper._allRows.index(of: oldRow as! BaseRow)! + // Remove the previous row from the form + kvoWrapper._allRows[oldRowIndex].willBeRemovedFromSection() + kvoWrapper._allRows[oldRowIndex] = newValue + } else { + kvoWrapper._allRows.append(newValue) + } + + kvoWrapper.rows[position] = newValue + newValue.wasAddedTo(section: self) + } } - public subscript (range: Range) -> [BaseRow] { - get { return kvoWrapper.rows.objects(at: IndexSet(integersIn: range)) as! [BaseRow] } - set { kvoWrapper.rows.replaceObjects(in: NSRange(range), withObjectsFrom: newValue) } + public subscript (range: Range) -> ArraySlice { + get { return kvoWrapper.rows.map { $0 as! BaseRow }[range] } + set { replaceSubrange(range, with: newValue) } } - public func index(after i: Int) -> Int {return i + 1} - public func index(before i: Int) -> Int {return i - 1} + public func index(after i: Int) -> Int { return i + 1 } + public func index(before i: Int) -> Int { return i - 1 } } -extension Section : RangeReplaceableCollection { - +extension Section: RangeReplaceableCollection { + // MARK: RangeReplaceableCollectionType - - public func append(_ formRow: BaseRow){ + + public func append(_ formRow: BaseRow) { kvoWrapper.rows.insert(formRow, at: kvoWrapper.rows.count) kvoWrapper._allRows.append(formRow) formRow.wasAddedTo(section: self) } - - public func append(contentsOf newElements: S) where S.Iterator.Element == BaseRow { + + public func append(contentsOf newElements: S) where S.Iterator.Element == BaseRow { kvoWrapper.rows.addObjects(from: newElements.map { $0 }) kvoWrapper._allRows.append(contentsOf: newElements) - for row in newElements{ + for row in newElements { row.wasAddedTo(section: self) } } - - public func replaceSubrange(_ subRange: Range, with newElements: C) where C.Iterator.Element == BaseRow { - for i in subRange.lowerBound..(_ subrange: Range, with newElements: C) where C : Collection, C.Element == BaseRow { + for i in subrange.lowerBound.. BaseRow { + let row = kvoWrapper.rows.object(at: position) as! BaseRow + row.willBeRemovedFromSection() + kvoWrapper.rows.removeObject(at: position) + if let index = kvoWrapper._allRows.index(of: row) { + kvoWrapper._allRows.remove(at: index) + } + + return row + } + private func indexForInsertion(at index: Int) -> Int { guard index != 0 else { return 0 } - + let row = kvoWrapper.rows[index-1] - if let i = kvoWrapper._allRows.index(of: row as! BaseRow){ + if let i = kvoWrapper._allRows.index(of: row as! BaseRow) { return i + 1 } return kvoWrapper._allRows.count @@ -283,29 +316,28 @@ extension Section : RangeReplaceableCollection { } extension Section /* Condition */{ - - //MARK: Hidden/Disable Engine - + + // MARK: Hidden/Disable Engine + /** Function that evaluates if the section should be hidden and updates it accordingly. */ - public final func evaluateHidden(){ + public final func evaluateHidden() { if let h = hidden, let f = form { switch h { - case .function(_ , let callback): + case .function(_, let callback): hiddenCache = callback(f) case .predicate(let predicate): hiddenCache = predicate.evaluate(with: self, substitutionVariables: f.dictionaryValuesToEvaluatePredicate()) } if hiddenCache { form?.hideSection(self) - } - else{ + } else { form?.showSection(self) } } } - + /** Internal function called when this section was added to a form. */ @@ -317,11 +349,11 @@ extension Section /* Condition */{ row.wasAddedTo(section: self) } } - + /** Internal function called to add this section to the observers of certain rows. Called when the hidden variable is set and depends on other rows. */ - func addToRowObservers(){ + func addToRowObservers() { guard let h = hidden else { return } switch h { case .function(let tags, _): @@ -330,22 +362,22 @@ extension Section /* Condition */{ form?.addRowObservers(to: self, rowTags: predicate.predicateVars, type: .hidden) } } - + /** Internal function called when this section was removed from a form. */ - func willBeRemovedFromForm(){ + func willBeRemovedFromForm() { for row in kvoWrapper._allRows { row.willBeRemovedFromForm() } removeFromRowObservers() self.form = nil } - + /** Internal function called to remove this section from the observers of certain rows. Called when the hidden variable is changed. */ - func removeFromRowObservers(){ + func removeFromRowObservers() { guard let h = hidden else { return } switch h { case .function(let tags, _): @@ -354,18 +386,18 @@ extension Section /* Condition */{ form?.removeRowObservers(from: self, rowTags: predicate.predicateVars, type: .hidden) } } - - func hide(row: BaseRow){ + + func hide(row: BaseRow) { row.baseCell.cellResignFirstResponder() (row as? BaseInlineRowType)?.collapseInlineRow() kvoWrapper.rows.remove(row) } - - func show(row: BaseRow){ + + func show(row: BaseRow) { guard !kvoWrapper.rows.contains(row) else { return } guard var index = kvoWrapper._allRows.index(of: row) else { return } var formIndex = NSNotFound - while (formIndex == NSNotFound && index > 0){ + while formIndex == NSNotFound && index > 0 { index = index - 1 let previous = kvoWrapper._allRows[index] formIndex = kvoWrapper.rows.index(of: previous) @@ -374,3 +406,87 @@ extension Section /* Condition */{ } } +/** + * Navigation options for a form view controller. + */ +public struct MultivaluedOptions: OptionSet { + + private enum Options: Int { + case none = 0, insert = 1, delete = 2, reorder = 4 + } + public let rawValue: Int + public init(rawValue: Int) { self.rawValue = rawValue} + private init(_ options: Options) { self.rawValue = options.rawValue } + + /// No multivalued. + public static let None = MultivaluedOptions(.none) + + /// Allows user to insert rows. + public static let Insert = MultivaluedOptions(.insert) + + /// Allows user to delete rows. + public static let Delete = MultivaluedOptions(.delete) + + /// Allows user to reorder rows + public static let Reorder = MultivaluedOptions(.reorder) +} + +/** + * Multivalued sections allows us to easily create insertable, deletable and reorderable sections. By using a multivalued section we can add multiple values for a certain field, such as telephone numbers in a contact. + */ +open class MultivaluedSection: Section { + + public var multivaluedOptions: MultivaluedOptions + public var showInsertIconInAddButton = true + public var addButtonProvider: ((MultivaluedSection) -> ButtonRow) = { _ in + return ButtonRow { + $0.title = "Add" + $0.cellStyle = .value1 + }.cellUpdate { cell, _ in + cell.textLabel?.textAlignment = .left + } + } + + public var multivaluedRowToInsertAt: ((Int) -> BaseRow)? + + public required init(multivaluedOptions: MultivaluedOptions = MultivaluedOptions.Insert.union(.Delete), + header: String = "", + footer: String = "", + _ initializer: (MultivaluedSection) -> Void = { _ in }) { + self.multivaluedOptions = multivaluedOptions + super.init(header: header, footer: footer, {section in initializer(section as! MultivaluedSection) }) + guard multivaluedOptions.contains(.Insert) else { return } + initialize() + } + + public required init() { + self.multivaluedOptions = MultivaluedOptions.Insert.union(.Delete) + super.init() + initialize() + } + + #if swift(>=4.1) + public required init(_ elements: S) where S : Sequence, S.Element == BaseRow { + self.multivaluedOptions = MultivaluedOptions.Insert.union(.Delete) + super.init(elements) + initialize() + } + #endif + + func initialize() { + let addRow = addButtonProvider(self) + addRow.onCellSelection { cell, row in + guard let tableView = cell.formViewController()?.tableView, let indexPath = row.indexPath else { return } + cell.formViewController()?.tableView(tableView, commit: .insert, forRowAt: indexPath) + } + self <<< addRow + } + /** + Method used to get all the values of the section. + + - returns: An Array mapping the row values. [value] + */ + public func values() -> [Any?] { + return kvoWrapper._allRows.filter({ $0.baseValue != nil }).map({ $0.baseValue }) + } +} diff --git a/Pods/Eureka/Source/Core/SelectableRowType.swift b/Pods/Eureka/Source/Core/SelectableRowType.swift index f931096..266f285 100644 --- a/Pods/Eureka/Source/Core/SelectableRowType.swift +++ b/Pods/Eureka/Source/Core/SelectableRowType.swift @@ -22,12 +22,11 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. - import Foundation /** * Every row that shall be used in a SelectableSection must conform to this protocol. */ -public protocol SelectableRowType : TypedRowType, RowType { - var selectableValue : Cell.Value? { get set } +public protocol SelectableRowType: RowType { + var selectableValue: Cell.Value? { get set } } diff --git a/Pods/Eureka/Source/Core/SelectableSection.swift b/Pods/Eureka/Source/Core/SelectableSection.swift index d20e24e..17fb018 100644 --- a/Pods/Eureka/Source/Core/SelectableSection.swift +++ b/Pods/Eureka/Source/Core/SelectableSection.swift @@ -22,7 +22,6 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. - import Foundation // MARK: SelectableSection @@ -34,12 +33,12 @@ import Foundation - SingleSelection: Only one selection at a time. Can additionally specify if deselection is enabled or not. */ public enum SelectionType { - + /** * Multiple options can be selected at once */ case multipleSelection - + /** * Only one selection at a time. Can additionally specify if deselection is enabled or not. */ @@ -51,48 +50,47 @@ public enum SelectionType { */ public protocol SelectableSectionType: Collection { associatedtype SelectableRow: BaseRow, SelectableRowType - + /// Defines how the selection works (single / multiple selection) - var selectionType : SelectionType { get set } - + var selectionType: SelectionType { get set } + /// A closure called when a row of this section is selected. var onSelectSelectableRow: ((SelectableRow.Cell, SelectableRow) -> Void)? { get set } - + func selectedRow() -> SelectableRow? func selectedRows() -> [SelectableRow] } -extension SelectableSectionType where Self: Section, Self.Iterator == IndexingIterator
, Self.Iterator.Element == BaseRow { - +extension SelectableSectionType where Self: Section { /** Returns the selected row of this section. Should be used if selectionType is SingleSelection */ public func selectedRow() -> SelectableRow? { return selectedRows().first } - + /** Returns the selected rows of this section. Should be used if selectionType is MultipleSelection */ public func selectedRows() -> [SelectableRow] { - return filter({ (row: BaseRow) -> Bool in - row is SelectableRow && row.baseValue != nil - }).map({ $0 as! SelectableRow}) + let selectedRows: [BaseRow] = self.filter { $0 is SelectableRow && $0.baseValue != nil } + return selectedRows.map { $0 as! SelectableRow } } - + /** Internal function used to set up a collection of rows before they are added to the section */ - func prepare(selectableRows rows: [BaseRow]){ + func prepare(selectableRows rows: [BaseRow]) { for row in rows { if let row = row as? SelectableRow { row.onCellSelection { [weak self] cell, row in - guard let s = self else { return } + guard let s = self, !row.isDisabled else { return } switch s.selectionType { case .multipleSelection: row.value = row.value == nil ? row.selectableValue : nil case let .singleSelection(enableDeselection): - s.filter { $0.baseValue != nil && $0 != row }.forEach { + s.forEach { + guard $0.baseValue != nil && $0 != row && $0 is SelectableRow else { return } $0.baseValue = nil $0.updateCell() } @@ -109,38 +107,47 @@ extension SelectableSectionType where Self: Section, Self.Iterator == IndexingIt } } } - + } /// A subclass of Section that serves to create a section with a list of selectable options. -open class SelectableSection : Section, SelectableSectionType where Row: BaseRow { - +open class SelectableSection: Section, SelectableSectionType where Row: SelectableRowType, Row: BaseRow { + public typealias SelectableRow = Row - + /// Defines how the selection works (single / multiple selection) public var selectionType = SelectionType.singleSelection(enableDeselection: true) - + /// A closure called when a row of this section is selected. public var onSelectSelectableRow: ((Row.Cell, Row) -> Void)? - - public required init(_ initializer: (Section) -> ()) { - super.init(initializer) + + public override init(_ initializer: @escaping (SelectableSection) -> Void) { + super.init({ _ in }) + initializer(self) } - - public init(_ header: String, selectionType: SelectionType, _ initializer: (Section) -> () = { _ in }) { + + public init(_ header: String, selectionType: SelectionType, _ initializer: @escaping (SelectableSection) -> Void = { _ in }) { self.selectionType = selectionType - super.init(header, initializer) + super.init(header, { _ in }) + initializer(self) } - public init(header: String, footer: String, selectionType: SelectionType, _ initializer: (Section) -> () = { _ in }) { + public init(header: String, footer: String, selectionType: SelectionType, _ initializer: @escaping (SelectableSection) -> Void = { _ in }) { self.selectionType = selectionType - super.init(header: header, footer: footer, initializer) + super.init(header: header, footer: footer, { _ in }) + initializer(self) } public required init() { - fatalError("init() has not been implemented") + super.init() + } + + #if swift(>=4.1) + public required init(_ elements: S) where S : Sequence, S.Element == BaseRow { + super.init(elements) } - + #endif + open override func rowsHaveBeenAdded(_ rows: [BaseRow], at: IndexSet) { prepare(selectableRows: rows) } diff --git a/Pods/Eureka/Source/Core/SwipeActions.swift b/Pods/Eureka/Source/Core/SwipeActions.swift new file mode 100644 index 0000000..33eefb4 --- /dev/null +++ b/Pods/Eureka/Source/Core/SwipeActions.swift @@ -0,0 +1,126 @@ +// +// Swipe.swift +// Eureka +// +// Created by Marco Betschart on 14.06.17. +// Copyright © 2017 Xmartlabs. All rights reserved. +// + +import Foundation + +public typealias SwipeActionHandler = (SwipeAction, BaseRow, ((Bool) -> Void)?) -> Void + +public class SwipeAction: ContextualAction { + let handler: SwipeActionHandler + let style: Style + + public var backgroundColor: UIColor? + public var image: UIImage? + public var title: String? + + public init(style: Style, title: String?, handler: @escaping SwipeActionHandler){ + self.style = style + self.title = title + self.handler = handler + } + + func contextualAction(forRow: BaseRow) -> ContextualAction { + var action: ContextualAction + if #available(iOS 11, *){ + action = UIContextualAction(style: style.contextualStyle as! UIContextualAction.Style, title: title){ [weak self] action, view, completion -> Void in + guard let strongSelf = self else{ return } + strongSelf.handler(strongSelf, forRow, completion) + } + } else { + action = UITableViewRowAction(style: style.contextualStyle as! UITableViewRowActionStyle,title: title){ [weak self] (action, indexPath) -> Void in + guard let strongSelf = self else{ return } + strongSelf.handler(strongSelf, forRow) { _ in + DispatchQueue.main.async { + guard action.style == .destructive else { + forRow.baseCell?.formViewController()?.tableView?.setEditing(false, animated: true) + return + } + forRow.section?.remove(at: indexPath.row) + } + } + } + } + if let color = self.backgroundColor { + action.backgroundColor = color + } + if let image = self.image { + action.image = image + } + return action + } + + public enum Style{ + case normal + case destructive + + var contextualStyle: ContextualStyle { + if #available(iOS 11, *){ + switch self{ + case .normal: + return UIContextualAction.Style.normal + case .destructive: + return UIContextualAction.Style.destructive + } + } else { + switch self{ + case .normal: + return UITableViewRowActionStyle.normal + case .destructive: + return UITableViewRowActionStyle.destructive + } + } + } + } +} + +public struct SwipeConfiguration { + + unowned var row: BaseRow + + init(_ row: BaseRow){ + self.row = row + } + + public var performsFirstActionWithFullSwipe = false + public var actions: [SwipeAction] = [] +} + +extension SwipeConfiguration { + @available(iOS 11.0, *) + var contextualConfiguration: UISwipeActionsConfiguration? { + let contextualConfiguration = UISwipeActionsConfiguration(actions: self.contextualActions as! [UIContextualAction]) + contextualConfiguration.performsFirstActionWithFullSwipe = self.performsFirstActionWithFullSwipe + return contextualConfiguration + } + + var contextualActions: [ContextualAction]{ + return self.actions.map { $0.contextualAction(forRow: self.row) } + } +} + +protocol ContextualAction { + var backgroundColor: UIColor? { get set } + var image: UIImage? { get set } + var title: String? { get set } +} + +extension UITableViewRowAction: ContextualAction { + public var image: UIImage? { + get { return nil } + set { return } + } +} + +@available(iOS 11.0, *) +extension UIContextualAction: ContextualAction {} + +public protocol ContextualStyle{} +extension UITableViewRowActionStyle: ContextualStyle {} + +@available(iOS 11.0, *) +extension UIContextualAction.Style: ContextualStyle {} diff --git a/Pods/Eureka/Source/Core/Validation.swift b/Pods/Eureka/Source/Core/Validation.swift index 7477a2c..0c0c442 100644 --- a/Pods/Eureka/Source/Core/Validation.swift +++ b/Pods/Eureka/Source/Core/Validation.swift @@ -24,21 +24,19 @@ import Foundation - public struct ValidationError: Equatable { - + public let msg: String - + public init(msg: String) { self.msg = msg } } -public func ==(lhs: ValidationError, rhs: ValidationError) -> Bool{ +public func == (lhs: ValidationError, rhs: ValidationError) -> Bool { return lhs.msg == rhs.msg } - public protocol BaseRuleType { var id: String? { get set } var validationError: ValidationError { get set } @@ -46,58 +44,56 @@ public protocol BaseRuleType { public protocol RuleType: BaseRuleType { associatedtype RowValueType - + func isValid(value: RowValueType?) -> ValidationError? } -public struct ValidationOptions : OptionSet { +public struct ValidationOptions: OptionSet { public let rawValue: Int - - public init(rawValue: Int){ + + public init(rawValue: Int) { self.rawValue = rawValue } - + public static let validatesOnDemand = ValidationOptions(rawValue: 1 << 0) public static let validatesOnChange = ValidationOptions(rawValue: 1 << 1) public static let validatesOnBlur = ValidationOptions(rawValue: 1 << 2) public static let validatesOnChangeAfterBlurred = ValidationOptions(rawValue: 1 << 3) - + public static let validatesAlways: ValidationOptions = [.validatesOnChange, .validatesOnBlur] } - -internal struct ValidationRuleHelper { +internal struct ValidationRuleHelper where T: Equatable { let validateFn: ((T?) -> ValidationError?) let rule: BaseRuleType } public struct RuleSet { - + internal var rules: [ValidationRuleHelper] = [] - - public init(){} - - public mutating func add(rule: Rule) where T == Rule.RowValueType{ + + public init() {} + + /// Add a validation Rule to a Row + /// - Parameter rule: RuleType object typed to the same type of the Row.value + public mutating func add(rule: Rule) where T == Rule.RowValueType { let validFn: ((T?) -> ValidationError?) = { (val: T?) in return rule.isValid(value: val) } rules.append(ValidationRuleHelper(validateFn: validFn, rule: rule)) } - + public mutating func remove(ruleWithIdentifier identifier: String) { if let index = rules.index(where: { (validationRuleHelper) -> Bool in return validationRuleHelper.rule.id == identifier - }){ + }) { rules.remove(at: index) } } - + public mutating func removeAllRules() { rules.removeAll() } } - - - diff --git a/Pods/Eureka/Source/Rows/ActionSheetRow.swift b/Pods/Eureka/Source/Rows/ActionSheetRow.swift index b0ea68e..67aa6e8 100644 --- a/Pods/Eureka/Source/Rows/ActionSheetRow.swift +++ b/Pods/Eureka/Source/Rows/ActionSheetRow.swift @@ -22,38 +22,39 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. - import Foundation -open class AlertSelectorCell : Cell, CellType { - +open class AlertSelectorCell : Cell, CellType where T: Equatable { + required public init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) } - + required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } - + open override func update() { super.update() accessoryType = .none editingAccessoryType = accessoryType selectionStyle = row.isDisabled ? .none : .default } - + open override func didSelect() { super.didSelect() row.deselect() } } -public class _ActionSheetRow: OptionsRow, PresenterRowType where Cell: BaseCell { +open class _ActionSheetRow: AlertOptionsRow, PresenterRowType where Cell: BaseCell { + + public typealias ProviderType = SelectorAlertController<_ActionSheetRow> - public var onPresentCallback : ((FormViewController, SelectorAlertController)->())? - lazy public var presentationMode: PresentationMode>? = { + public var onPresentCallback: ((FormViewController, ProviderType) -> Void)? + lazy public var presentationMode: PresentationMode? = { return .presentModally(controllerProvider: ControllerProvider.callback { [weak self] in - let vc = SelectorAlertController(title: self?.selectorTitle, message: nil, preferredStyle: .actionSheet) + let vc = SelectorAlertController<_ActionSheetRow>(title: self?.selectorTitle, message: nil, preferredStyle: .actionSheet) if let popView = vc.popoverPresentationController { guard let cell = self?.cell, let tableView = cell.formViewController()?.tableView else { fatalError() } popView.sourceView = tableView @@ -61,26 +62,25 @@ public class _ActionSheetRow: OptionsRow, PresenterRowType } vc.row = self return vc - }, - onDismiss: { [weak self] in - $0.dismiss(animated: true) - self?.cell?.formViewController()?.tableView?.reloadData() - }) + }, + onDismiss: { [weak self] in + $0.dismiss(animated: true) + self?.cell?.formViewController()?.tableView?.reloadData() + }) }() - + public required init(tag: String?) { super.init(tag: tag) } - - public override func customDidSelect() { + + open override func customDidSelect() { super.customDidSelect() if let presentationMode = presentationMode, !isDisabled { - if let controller = presentationMode.makeController(){ + if let controller = presentationMode.makeController() { controller.row = self onPresentCallback?(cell.formViewController()!, controller) presentationMode.present(controller, row: self, presentingController: cell.formViewController()!) - } - else{ + } else { presentationMode.present(nil, row: self, presentingController: cell.formViewController()!) } } @@ -88,9 +88,8 @@ public class _ActionSheetRow: OptionsRow, PresenterRowType } /// An options row where the user can select an option from an ActionSheet -public final class ActionSheetRow: _ActionSheetRow>, RowType { +public final class ActionSheetRow: _ActionSheetRow>, RowType where T: Equatable { required public init(tag: String?) { super.init(tag: tag) } } - diff --git a/Pods/Eureka/Source/Rows/AlertRow.swift b/Pods/Eureka/Source/Rows/AlertRow.swift index 724f5ff..fc40a74 100644 --- a/Pods/Eureka/Source/Rows/AlertRow.swift +++ b/Pods/Eureka/Source/Rows/AlertRow.swift @@ -24,34 +24,34 @@ import Foundation -open class _AlertRow: OptionsRow, PresenterRowType where Cell: BaseCell { +open class _AlertRow: AlertOptionsRow, PresenterRowType where Cell: BaseCell { + + public typealias PresentedController = SelectorAlertController<_AlertRow> - open var onPresentCallback : ((FormViewController, SelectorAlertController)->())? - lazy open var presentationMode: PresentationMode>? = { - return .presentModally(controllerProvider: ControllerProvider.callback { [weak self] in - let vc = SelectorAlertController(title: self?.selectorTitle, message: nil, preferredStyle: .alert) + open var onPresentCallback: ((FormViewController, PresentedController) -> Void)? + lazy open var presentationMode: PresentationMode? = { + return .presentModally(controllerProvider: ControllerProvider.callback { [weak self] in + let vc = PresentedController(title: self?.selectorTitle, message: nil, preferredStyle: .alert) vc.row = self return vc - }, onDismiss: { [weak self] in - $0.dismiss(animated: true) - self?.cell?.formViewController()?.tableView?.reloadData() - } - ) + }, onDismiss: { [weak self] in + $0.dismiss(animated: true) + self?.cell?.formViewController()?.tableView?.reloadData() + }) }() - + public required init(tag: String?) { super.init(tag: tag) } - + open override func customDidSelect() { super.customDidSelect() - if let presentationMode = presentationMode, !isDisabled { - if let controller = presentationMode.makeController(){ + if let presentationMode = presentationMode, !isDisabled { + if let controller = presentationMode.makeController() { controller.row = self onPresentCallback?(cell.formViewController()!, controller) presentationMode.present(controller, row: self, presentingController: cell.formViewController()!) - } - else{ + } else { presentationMode.present(nil, row: self, presentingController: cell.formViewController()!) } } @@ -64,4 +64,3 @@ public final class AlertRow: _AlertRow>, RowT super.init(tag: tag) } } - diff --git a/Pods/Eureka/Source/Rows/ButtonRow.swift b/Pods/Eureka/Source/Rows/ButtonRow.swift index 44ca56b..0e3ddfd 100644 --- a/Pods/Eureka/Source/Rows/ButtonRow.swift +++ b/Pods/Eureka/Source/Rows/ButtonRow.swift @@ -27,27 +27,24 @@ import Foundation // MARK: ButtonCell open class ButtonCellOf: Cell, CellType { - + required public init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) } - + required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } - + open override func update() { super.update() selectionStyle = row.isDisabled ? .none : .default accessoryType = .none editingAccessoryType = accessoryType textLabel?.textAlignment = .center - textLabel?.textColor = tintColor - var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0 - tintColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha) - textLabel?.textColor = UIColor(red: red, green: green, blue: blue, alpha: row.isDisabled ? 0.3 : 1.0) + textLabel?.textColor = tintColor.withAlphaComponent(row.isDisabled ? 0.3 : 1.0) } - + open override func didSelect() { super.didSelect() row.deselect() @@ -56,52 +53,42 @@ open class ButtonCellOf: Cell, CellType { public typealias ButtonCell = ButtonCellOf - -//MARK: ButtonRow +// MARK: ButtonRow open class _ButtonRowOf : Row> { open var presentationMode: PresentationMode? - + required public init(tag: String?) { super.init(tag: tag) displayValueFor = nil cellStyle = .default } - + open override func customDidSelect() { super.customDidSelect() if !isDisabled { if let presentationMode = presentationMode { - if let controller = presentationMode.makeController(){ + if let controller = presentationMode.makeController() { presentationMode.present(controller, row: self, presentingController: self.cell.formViewController()!) - } - else{ + } else { presentationMode.present(nil, row: self, presentingController: self.cell.formViewController()!) } } } } - + open override func customUpdateCell() { super.customUpdateCell() let leftAligmnment = presentationMode != nil cell.textLabel?.textAlignment = leftAligmnment ? .left : .center cell.accessoryType = !leftAligmnment || isDisabled ? .none : .disclosureIndicator cell.editingAccessoryType = cell.accessoryType - if (!leftAligmnment){ - var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0 - cell.tintColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha) - cell.textLabel?.textColor = UIColor(red: red, green: green, blue: blue, alpha:isDisabled ? 0.3 : 1.0) - } - else{ - cell.textLabel?.textColor = nil - } + cell.textLabel?.textColor = !leftAligmnment ? cell.tintColor.withAlphaComponent(isDisabled ? 0.3 : 1.0) : nil } - + open override func prepare(for segue: UIStoryboardSegue) { super.prepare(for: segue) - let rowVC = segue.destination as? RowControllerType - rowVC?.onDismissCallback = presentationMode?.onDismissCallback + (segue.destination as? RowControllerType)?.onDismissCallback = presentationMode?.onDismissCallback } } diff --git a/Pods/Eureka/Source/Rows/ButtonRowWithPresent.swift b/Pods/Eureka/Source/Rows/ButtonRowWithPresent.swift index 2c849cb..490f3af 100644 --- a/Pods/Eureka/Source/Rows/ButtonRowWithPresent.swift +++ b/Pods/Eureka/Source/Rows/ButtonRowWithPresent.swift @@ -25,62 +25,59 @@ import Foundation open class _ButtonRowWithPresent: Row>, PresenterRowType where VCType: UIViewController { - + open var presentationMode: PresentationMode? - open var onPresentCallback : ((FormViewController, VCType)->())? - + open var onPresentCallback: ((FormViewController, VCType) -> Void)? + required public init(tag: String?) { super.init(tag: tag) displayValueFor = nil cellStyle = .default } - + open override func customUpdateCell() { super.customUpdateCell() let leftAligmnment = presentationMode != nil cell.textLabel?.textAlignment = leftAligmnment ? .left : .center cell.accessoryType = !leftAligmnment || isDisabled ? .none : .disclosureIndicator cell.editingAccessoryType = cell.accessoryType - if (!leftAligmnment){ + if !leftAligmnment { var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0 cell.tintColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha) cell.textLabel?.textColor = UIColor(red: red, green: green, blue: blue, alpha:isDisabled ? 0.3 : 1.0) - } - else{ + } else { cell.textLabel?.textColor = nil } } - + open override func customDidSelect() { super.customDidSelect() if let presentationMode = presentationMode, !isDisabled { - if let controller = presentationMode.makeController(){ + if let controller = presentationMode.makeController() { controller.row = self onPresentCallback?(cell.formViewController()!, controller) presentationMode.present(controller, row: self, presentingController: cell.formViewController()!) - } - else{ + } else { presentationMode.present(nil, row: self, presentingController: cell.formViewController()!) } } } - + open override func prepare(for segue: UIStoryboardSegue) { super.prepare(for: segue) guard let rowVC = segue.destination as? VCType else { return } - if let callback = presentationMode?.onDismissCallback{ + if let callback = presentationMode?.onDismissCallback { rowVC.onDismissCallback = callback } rowVC.row = self onPresentCallback?(cell.formViewController()!, rowVC) } - -} +} -//MARK: Rows +// MARK: Rows /// A generic row with a button that presents a view controller when tapped public final class ButtonRowWithPresent : _ButtonRowWithPresent, RowType where VCType: UIViewController { @@ -88,6 +85,3 @@ public final class ButtonRowWithPresent : _Butto super.init(tag: tag) } } - - - diff --git a/Pods/Eureka/Source/Rows/CheckRow.swift b/Pods/Eureka/Source/Rows/CheckRow.swift index 9393477..e9f329a 100644 --- a/Pods/Eureka/Source/Rows/CheckRow.swift +++ b/Pods/Eureka/Source/Rows/CheckRow.swift @@ -26,50 +26,47 @@ import Foundation // MARK: CheckCell -public final class CheckCell : Cell, CellType { - +public final class CheckCell: Cell, CellType { + required public init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) } - + required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } - + open override func update() { super.update() accessoryType = row.value == true ? .checkmark : .none editingAccessoryType = accessoryType selectionStyle = .default - var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0 - tintColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha) if row.isDisabled { - tintColor = UIColor(red: red, green: green, blue: blue, alpha: 0.3) + tintAdjustmentMode = .dimmed selectionStyle = .none - } - else { - tintColor = UIColor(red: red, green: green, blue: blue, alpha: 1) + } else { + tintAdjustmentMode = .automatic } } - + open override func setup() { super.setup() accessoryType = .checkmark editingAccessoryType = accessoryType } - + open override func didSelect() { row.value = row.value ?? false ? false : true row.deselect() row.updateCell() } - + } // MARK: CheckRow open class _CheckRow: Row { - + required public init(tag: String?) { super.init(tag: tag) displayValueFor = nil @@ -78,7 +75,7 @@ open class _CheckRow: Row { ///// Boolean row that has a checkmark as accessoryType public final class CheckRow: _CheckRow, RowType { - + required public init(tag: String?) { super.init(tag: tag) } diff --git a/Pods/Eureka/Source/Rows/Common/AlertOptionsRow.swift b/Pods/Eureka/Source/Rows/Common/AlertOptionsRow.swift new file mode 100644 index 0000000..46d12ed --- /dev/null +++ b/Pods/Eureka/Source/Rows/Common/AlertOptionsRow.swift @@ -0,0 +1,41 @@ +// +// AlertOptionsRow.swift +// Eureka ( https://github.com/xmartlabs/Eureka ) +// +// Copyright (c) 2016 Xmartlabs SRL ( http://xmartlabs.com ) +// +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + + +import Foundation + +open class AlertOptionsRow : OptionsRow, AlertOptionsProviderRow where Cell: BaseCell { + + typealias OptionsProviderType = OptionsProvider + + open var cancelTitle: String? + + required public init(tag: String?) { + super.init(tag: tag) + } + +} diff --git a/Pods/Eureka/Source/Rows/Common/DateFieldRow.swift b/Pods/Eureka/Source/Rows/Common/DateFieldRow.swift index 5a6f1e6..e2ee1a1 100644 --- a/Pods/Eureka/Source/Rows/Common/DateFieldRow.swift +++ b/Pods/Eureka/Source/Rows/Common/DateFieldRow.swift @@ -25,25 +25,25 @@ import Foundation public protocol DatePickerRowProtocol: class { - var minimumDate : Date? { get set } - var maximumDate : Date? { get set } - var minuteInterval : Int? { get set } + var minimumDate: Date? { get set } + var maximumDate: Date? { get set } + var minuteInterval: Int? { get set } } +open class DateCell: Cell, CellType { -open class DateCell : Cell, CellType { - public var datePicker: UIDatePicker - + public required init(style: UITableViewCellStyle, reuseIdentifier: String?) { datePicker = UIDatePicker() super.init(style: style, reuseIdentifier: reuseIdentifier) } - + required public init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") + datePicker = UIDatePicker() + super.init(coder: aDecoder) } - + open override func setup() { super.setup() accessoryType = .none @@ -51,43 +51,43 @@ open class DateCell : Cell, CellType { datePicker.datePickerMode = datePickerMode() datePicker.addTarget(self, action: #selector(DateCell.datePickerValueChanged(_:)), for: .valueChanged) } - + deinit { datePicker.removeTarget(self, action: nil, for: .allEvents) } - + open override func update() { super.update() selectionStyle = row.isDisabled ? .none : .default datePicker.setDate(row.value ?? Date(), animated: row is CountDownPickerRow) datePicker.minimumDate = (row as? DatePickerRowProtocol)?.minimumDate datePicker.maximumDate = (row as? DatePickerRowProtocol)?.maximumDate - if let minuteIntervalValue = (row as? DatePickerRowProtocol)?.minuteInterval{ + if let minuteIntervalValue = (row as? DatePickerRowProtocol)?.minuteInterval { datePicker.minuteInterval = minuteIntervalValue } if row.isHighlighted { textLabel?.textColor = tintColor } } - + open override func didSelect() { super.didSelect() row.deselect() } - - override open var inputView : UIView? { - if let v = row.value{ + + override open var inputView: UIView? { + if let v = row.value { datePicker.setDate(v, animated:row is CountDownRow) } return datePicker } - - func datePickerValueChanged(_ sender: UIDatePicker){ + + @objc func datePickerValueChanged(_ sender: UIDatePicker) { row.value = sender.date detailTextLabel?.text = row.displayValueFor?(row.value) } - - private func datePickerMode() -> UIDatePickerMode{ + + private func datePickerMode() -> UIDatePickerMode { switch row { case is DateRow: return .date @@ -101,35 +101,32 @@ open class DateCell : Cell, CellType { return .date } } - + open override func cellCanBecomeFirstResponder() -> Bool { return canBecomeFirstResponder } override open var canBecomeFirstResponder: Bool { - get { - return !row.isDisabled - } + return !row.isDisabled } } - open class _DateFieldRow: Row, DatePickerRowProtocol, NoValueDisplayTextConformance { - + /// The minimum value for this row's UIDatePicker - open var minimumDate : Date? - + open var minimumDate: Date? + /// The maximum value for this row's UIDatePicker - open var maximumDate : Date? - + open var maximumDate: Date? + /// The interval between options for this row's UIDatePicker - open var minuteInterval : Int? - + open var minuteInterval: Int? + /// The formatter for the date picked by the user open var dateFormatter: DateFormatter? - + open var noValueDisplayText: String? = nil - + required public init(tag: String?) { super.init(tag: tag) displayValueFor = { [unowned self] value in diff --git a/Pods/Eureka/Source/Rows/Common/DateInlineFieldRow.swift b/Pods/Eureka/Source/Rows/Common/DateInlineFieldRow.swift index bacb4de..7154a15 100644 --- a/Pods/Eureka/Source/Rows/Common/DateInlineFieldRow.swift +++ b/Pods/Eureka/Source/Rows/Common/DateInlineFieldRow.swift @@ -24,51 +24,49 @@ import Foundation +open class DateInlineCell: Cell, CellType { -open class DateInlineCell : Cell, CellType { - public required init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) } - + required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } - + open override func setup() { super.setup() accessoryType = .none editingAccessoryType = .none } - + open override func update() { super.update() selectionStyle = row.isDisabled ? .none : .default } - + open override func didSelect() { super.didSelect() row.deselect() } } - open class _DateInlineFieldRow: Row, DatePickerRowProtocol, NoValueDisplayTextConformance { - + /// The minimum value for this row's UIDatePicker - open var minimumDate : Date? - + open var minimumDate: Date? + /// The maximum value for this row's UIDatePicker - open var maximumDate : Date? - + open var maximumDate: Date? + /// The interval between options for this row's UIDatePicker - open var minuteInterval : Int? - + open var minuteInterval: Int? + /// The formatter for the date picked by the user open var dateFormatter: DateFormatter? - + open var noValueDisplayText: String? - + required public init(tag: String?) { super.init(tag: tag) dateFormatter = DateFormatter() diff --git a/Pods/Eureka/Source/Rows/Common/DecimalFormatter.swift b/Pods/Eureka/Source/Rows/Common/DecimalFormatter.swift index 47d61ab..8087e77 100644 --- a/Pods/Eureka/Source/Rows/Common/DecimalFormatter.swift +++ b/Pods/Eureka/Source/Rows/Common/DecimalFormatter.swift @@ -24,8 +24,10 @@ import Foundation -open class DecimalFormatter : NumberFormatter, FormatterProtocol { - +/// A custom formatter for numbers with two digits after the decimal mark +open class DecimalFormatter: NumberFormatter, FormatterProtocol { + + /// Creates the formatter with 2 Fraction Digits, Locale set to .current and .decimal NumberFormatter.Style public override init() { super.init() locale = Locale.current @@ -33,18 +35,24 @@ open class DecimalFormatter : NumberFormatter, FormatterProtocol { minimumFractionDigits = 2 maximumFractionDigits = 2 } - + required public init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + + /// Creates an NSNumber from the given String + /// - Parameter obj: Pointer to NSNumber object to assign + /// - Parameter for: String with number assumed to have the configured min. fraction digits. + /// - Parameter range: Unused range parameter override open func getObjectValue(_ obj: AutoreleasingUnsafeMutablePointer?, for string: String, range rangep: UnsafeMutablePointer?) throws { guard obj != nil else { return } let str = string.components(separatedBy: CharacterSet.decimalDigits.inverted).joined(separator: "") + // Recover the number from the string in a way that forces the formatter's fraction digits + // numberWithoutDecimals / 10 ^ minimumFractionDigits obj?.pointee = NSNumber(value: (Double(str) ?? 0.0)/Double(pow(10.0, Double(minimumFractionDigits)))) } - + open func getNewPosition(forPosition position: UITextPosition, inTextInput textInput: UITextInput, oldValue: String?, newValue: String?) -> UITextPosition { - return textInput.position(from: position, offset:((newValue?.characters.count ?? 0) - (oldValue?.characters.count ?? 0))) ?? position + return textInput.position(from: position, offset:((newValue?.count ?? 0) - (oldValue?.count ?? 0))) ?? position } } diff --git a/Pods/Eureka/Source/Rows/Common/FieldRow.swift b/Pods/Eureka/Source/Rows/Common/FieldRow.swift index 811aa2a..99882cd 100644 --- a/Pods/Eureka/Source/Rows/Common/FieldRow.swift +++ b/Pods/Eureka/Source/Rows/Common/FieldRow.swift @@ -29,45 +29,43 @@ public protocol InputTypeInitiable { } public protocol FieldRowConformance : FormatterConformance { - var textFieldPercentage : CGFloat? { get set } + var titlePercentage : CGFloat? { get set } var placeholder : String? { get set } var placeholderColor : UIColor? { get set } } extension Int: InputTypeInitiable { - - public init?(string stringValue: String){ + + public init?(string stringValue: String) { self.init(stringValue, radix: 10) } } extension Float: InputTypeInitiable { - public init?(string stringValue: String){ + public init?(string stringValue: String) { self.init(stringValue) } } extension String: InputTypeInitiable { - public init?(string stringValue: String){ + public init?(string stringValue: String) { self.init(stringValue) } } extension URL: InputTypeInitiable {} extension Double: InputTypeInitiable { - public init?(string stringValue: String){ + public init?(string stringValue: String) { self.init(stringValue) } } - open class FormatteableRow: Row, FormatterConformance where Cell: BaseCell, Cell: TextInputCell { - - + /// A formatter to be used to format the user's input open var formatter: Formatter? - + /// If the formatter should be used while the user is editing the text. open var useFormatterDuringInput = false open var useFormatterOnDidBeginEditing: Bool? - + public required init(tag: String?) { super.init(tag: tag) displayValueFor = { [unowned self] value in @@ -82,21 +80,31 @@ open class FormatteableRow: Row, FormatterConformance wher } - open class FieldRow: FormatteableRow, FieldRowConformance, KeyboardReturnHandler where Cell: BaseCell, Cell: TextFieldCell { - + /// Configuration for the keyboardReturnType of this row - open var keyboardReturnType : KeyboardReturnTypeConfiguration? - + open var keyboardReturnType: KeyboardReturnTypeConfiguration? + /// The percentage of the cell that should be occupied by the textField - open var textFieldPercentage : CGFloat? - + @available (*, deprecated, message: "Use titleLabelPercentage instead") + open var textFieldPercentage : CGFloat? { + get { + return titlePercentage.map { 1 - $0 } + } + set { + titlePercentage = newValue.map { 1 - $0 } + } + } + + /// The percentage of the cell that should be occupied by the title (i.e. the titleLabel and optional imageView combined) + open var titlePercentage: CGFloat? + /// The placeholder for the textField - open var placeholder : String? - + open var placeholder: String? + /// The textColor for the textField's placeholder - open var placeholderColor : UIColor? - + open var placeholderColor: UIColor? + public required init(tag: String?) { super.init(tag: tag) } @@ -106,68 +114,77 @@ open class FieldRow: FormatteableRow, FieldRowConformance, * Protocol for cells that contain a UITextField */ public protocol TextInputCell { - var textInput : UITextInput { get } + var textInput: UITextInput { get } } public protocol TextFieldCell: TextInputCell { - - var textField: UITextField { get } + var textField: UITextField! { get } } extension TextFieldCell { - + public var textInput: UITextInput { return textField } } open class _FieldCell : Cell, UITextFieldDelegate, TextFieldCell where T: Equatable, T: InputTypeInitiable { - - public var textField: UITextField - - open var titleLabel : UILabel? { - textLabel?.translatesAutoresizingMaskIntoConstraints = false - textLabel?.setContentHuggingPriority(500, for: .horizontal) - textLabel?.setContentCompressionResistancePriority(1000, for: .horizontal) - return textLabel - } - fileprivate var observingTitleText: Bool = false + @IBOutlet public weak var textField: UITextField! + @IBOutlet public weak var titleLabel: UILabel? + + fileprivate var observingTitleText = false + private var awakeFromNibCalled = false open var dynamicConstraints = [NSLayoutConstraint]() - + + private var calculatedTitlePercentage: CGFloat = 0.7 + public required init(style: UITableViewCellStyle, reuseIdentifier: String?) { - - textField = UITextField() + + let textField = UITextField() + self.textField = textField textField.translatesAutoresizingMaskIntoConstraints = false - + super.init(style: style, reuseIdentifier: reuseIdentifier) - - NotificationCenter.default.addObserver(forName: Notification.Name.UIApplicationWillResignActive, object: nil, queue: nil){ [weak self] notification in + + setupTitleLabel() + + contentView.addSubview(titleLabel!) + contentView.addSubview(textField) + + NotificationCenter.default.addObserver(forName: Notification.Name.UIApplicationWillResignActive, object: nil, queue: nil) { [weak self] _ in guard let me = self else { return } guard me.observingTitleText else { return } me.titleLabel?.removeObserver(me, forKeyPath: "text") me.observingTitleText = false } - NotificationCenter.default.addObserver(forName: Notification.Name.UIApplicationDidBecomeActive, object: nil, queue: nil){ [weak self] notification in + NotificationCenter.default.addObserver(forName: Notification.Name.UIApplicationDidBecomeActive, object: nil, queue: nil) { [weak self] _ in guard let me = self else { return } guard !me.observingTitleText else { return } me.titleLabel?.addObserver(me, forKeyPath: "text", options: NSKeyValueObservingOptions.old.union(.new), context: nil) me.observingTitleText = true } - - NotificationCenter.default.addObserver(forName: Notification.Name.UIContentSizeCategoryDidChange, object: nil, queue: nil){ [weak self] notification in + + NotificationCenter.default.addObserver(forName: Notification.Name.UIContentSizeCategoryDidChange, object: nil, queue: nil) { [weak self] _ in + self?.setupTitleLabel() self?.setNeedsUpdateConstraints() } } - + required public init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") + super.init(coder: aDecoder) + } + + open override func awakeFromNib() { + super.awakeFromNib() + awakeFromNibCalled = true } - + deinit { - textField.delegate = nil - textField.removeTarget(self, action: nil, for: .allEvents) + textField?.delegate = nil + textField?.removeTarget(self, action: nil, for: .allEvents) + guard !awakeFromNibCalled else { return } if observingTitleText { titleLabel?.removeObserver(self, forKeyPath: "text") } @@ -176,30 +193,38 @@ open class _FieldCell : Cell, UITextFieldDelegate, TextFieldCell where T: NotificationCenter.default.removeObserver(self, name: Notification.Name.UIApplicationDidBecomeActive, object: nil) NotificationCenter.default.removeObserver(self, name: Notification.Name.UIContentSizeCategoryDidChange, object: nil) } - + open override func setup() { super.setup() selectionStyle = .none - contentView.addSubview(titleLabel!) - contentView.addSubview(textField) - titleLabel?.addObserver(self, forKeyPath: "text", options: NSKeyValueObservingOptions.old.union(.new), context: nil) - observingTitleText = true - imageView?.addObserver(self, forKeyPath: "image", options: NSKeyValueObservingOptions.old.union(.new), context: nil) + if !awakeFromNibCalled { + titleLabel?.addObserver(self, forKeyPath: "text", options: NSKeyValueObservingOptions.old.union(.new), context: nil) + observingTitleText = true + imageView?.addObserver(self, forKeyPath: "image", options: NSKeyValueObservingOptions.old.union(.new), context: nil) + } textField.addTarget(self, action: #selector(_FieldCell.textFieldDidChange(_:)), for: .editingChanged) - + } - + open override func update() { super.update() detailTextLabel?.text = nil - if let title = row.title { - textField.textAlignment = title.isEmpty ? .left : .right - textField.clearButtonMode = title.isEmpty ? .whileEditing : .never - } - else{ - textField.textAlignment = .left - textField.clearButtonMode = .whileEditing + + if !awakeFromNibCalled { + if let title = row.title { + switch row.cellStyle { + case .subtitle: + textField.textAlignment = .left + textField.clearButtonMode = .whileEditing + default: + textField.textAlignment = title.isEmpty ? .left : .right + textField.clearButtonMode = title.isEmpty ? .whileEditing : .never + } + } else { + textField.textAlignment = .left + textField.clearButtonMode = .whileEditing + } } textField.delegate = self textField.text = row.displayValueFor?(row.value) @@ -208,9 +233,8 @@ open class _FieldCell : Cell, UITextFieldDelegate, TextFieldCell where T: textField.font = .preferredFont(forTextStyle: .body) if let placeholder = (row as? FieldRowConformance)?.placeholder { if let color = (row as? FieldRowConformance)?.placeholderColor { - textField.attributedPlaceholder = NSAttributedString(string: placeholder, attributes: [NSForegroundColorAttributeName: color]) - } - else{ + textField.attributedPlaceholder = NSAttributedString(string: placeholder, attributes: [NSAttributedStringKey.foregroundColor: color]) + } else { textField.placeholder = (row as? FieldRowConformance)?.placeholder } } @@ -218,71 +242,118 @@ open class _FieldCell : Cell, UITextFieldDelegate, TextFieldCell where T: textLabel?.textColor = tintColor } } - + open override func cellCanBecomeFirstResponder() -> Bool { - return !row.isDisabled && textField.canBecomeFirstResponder + return !row.isDisabled && textField?.canBecomeFirstResponder == true } - + open override func cellBecomeFirstResponder(withDirection: Direction) -> Bool { - return textField.becomeFirstResponder() + return textField?.becomeFirstResponder() ?? false } - + open override func cellResignFirstResponder() -> Bool { - return textField.resignFirstResponder() + return textField?.resignFirstResponder() ?? true } open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { let obj = object as AnyObject? - - if let keyPathValue = keyPath, let changeType = change?[NSKeyValueChangeKey.kindKey], ((obj === titleLabel && keyPathValue == "text") || (obj === imageView && keyPathValue == "image")) && (changeType as? NSNumber)?.uintValue == NSKeyValueChange.setting.rawValue { + + if let keyPathValue = keyPath, let changeType = change?[NSKeyValueChangeKey.kindKey], + ((obj === titleLabel && keyPathValue == "text") || (obj === imageView && keyPathValue == "image")) && + (changeType as? NSNumber)?.uintValue == NSKeyValueChange.setting.rawValue { setNeedsUpdateConstraints() updateConstraintsIfNeeded() } } - - // Mark: Helpers - + + // MARK: Helpers + open func customConstraints() { + + guard !awakeFromNibCalled else { return } contentView.removeConstraints(dynamicConstraints) dynamicConstraints = [] - var views : [String: AnyObject] = ["textField": textField] - dynamicConstraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|-11-[textField]-11-|", options: .alignAllLastBaseline, metrics: nil, views: ["textField": textField]) - if let label = titleLabel, let text = label.text, !text.isEmpty { - dynamicConstraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|-11-[titleLabel]-11-|", options: .alignAllLastBaseline, metrics: nil, views: ["titleLabel": label]) - dynamicConstraints.append(NSLayoutConstraint(item: label, attribute: .centerY, relatedBy: .equal, toItem: textField, attribute: .centerY, multiplier: 1, constant: 0)) - } - if let imageView = imageView, let _ = imageView.image { - views["imageView"] = imageView + switch row.cellStyle { + case .subtitle: + var views: [String: AnyObject] = ["textField": textField] + if let titleLabel = titleLabel, let text = titleLabel.text, !text.isEmpty { - views["label"] = titleLabel - dynamicConstraints += NSLayoutConstraint.constraints(withVisualFormat: "H:[imageView]-(15)-[label]-[textField]-|", options: NSLayoutFormatOptions(), metrics: nil, views: views) - dynamicConstraints.append(NSLayoutConstraint(item: textField, attribute: .width, relatedBy: (row as? FieldRowConformance)?.textFieldPercentage != nil ? .equal : .greaterThanOrEqual, toItem: contentView, attribute: .width, multiplier: (row as? FieldRowConformance)?.textFieldPercentage ?? 0.3, constant: 0.0)) + views["titleLabel"] = titleLabel + dynamicConstraints += NSLayoutConstraint.constraints(withVisualFormat: "V:[titleLabel]-3-[textField]", options: .alignAllLeading, metrics: nil, views: views) + // Here we are centering the textField with an offset of -4. This replicates the exact behavior of the default UITableViewCell with .subtitle style + dynamicConstraints.append(NSLayoutConstraint(item: textField, attribute: .top, relatedBy: .equal, toItem: contentView, attribute: .centerY, multiplier: 1, constant: -4)) + dynamicConstraints.append(NSLayoutConstraint(item: titleLabel, attribute: .centerX, relatedBy: .equal, toItem: textField, attribute: .centerX, multiplier: 1, constant: 0)) + } else { + dynamicConstraints.append(NSLayoutConstraint(item: textField, attribute: .centerY, relatedBy: .equal, toItem: contentView, attribute: .centerY, multiplier: 1, constant: 0)) } - else{ - dynamicConstraints += NSLayoutConstraint.constraints(withVisualFormat: "H:[imageView]-(15)-[textField]-|", options: [], metrics: nil, views: views) + + if let imageView = imageView, let _ = imageView.image { + views["imageView"] = imageView + if let titleLabel = titleLabel, let text = titleLabel.text, !text.isEmpty { + dynamicConstraints += NSLayoutConstraint.constraints(withVisualFormat: "H:[imageView]-(15)-[titleLabel]-|", options: [], metrics: nil, views: views) + dynamicConstraints += NSLayoutConstraint.constraints(withVisualFormat: "H:[imageView]-(15)-[textField]-|", options: [], metrics: nil, views: views) + } else { + dynamicConstraints += NSLayoutConstraint.constraints(withVisualFormat: "H:[imageView]-(15)-[textField]-|", options: [], metrics: nil, views: views) + } + } else { + if let titleLabel = titleLabel, let text = titleLabel.text, !text.isEmpty { + dynamicConstraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|-[titleLabel]-|", options: [], metrics: nil, views: views) + dynamicConstraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|-[textField]-|", options: [], metrics: nil, views: views) + } else { + dynamicConstraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|-[textField]-|", options: .alignAllLeft, metrics: nil, views: views) + } } - } - else{ + + default: + var views: [String: AnyObject] = ["textField": textField] + dynamicConstraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|-11-[textField]-11-|", options: .alignAllLastBaseline, metrics: nil, views: views) + if let titleLabel = titleLabel, let text = titleLabel.text, !text.isEmpty { - views["label"] = titleLabel - dynamicConstraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|-[label]-[textField]-|", options: [], metrics: nil, views: views) - dynamicConstraints.append(NSLayoutConstraint(item: textField, attribute: .width, relatedBy: (row as? FieldRowConformance)?.textFieldPercentage != nil ? .equal : .greaterThanOrEqual, toItem: contentView, attribute: .width, multiplier: (row as? FieldRowConformance)?.textFieldPercentage ?? 0.3, constant: 0.0)) + views["titleLabel"] = titleLabel + dynamicConstraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|-11-[titleLabel]-11-|", options: .alignAllLastBaseline, metrics: nil, views: views) + dynamicConstraints.append(NSLayoutConstraint(item: titleLabel, attribute: .centerY, relatedBy: .equal, toItem: textField, attribute: .centerY, multiplier: 1, constant: 0)) } - else{ - dynamicConstraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|-[textField]-|", options: .alignAllLeft, metrics: nil, views: views) + + if let imageView = imageView, let _ = imageView.image { + views["imageView"] = imageView + if let titleLabel = titleLabel, let text = titleLabel.text, !text.isEmpty { + dynamicConstraints += NSLayoutConstraint.constraints(withVisualFormat: "H:[imageView]-(15)-[titleLabel]-[textField]-|", options: [], metrics: nil, views: views) + dynamicConstraints.append(NSLayoutConstraint(item: titleLabel, + attribute: .width, + relatedBy: (row as? FieldRowConformance)?.titlePercentage != nil ? .equal : .lessThanOrEqual, + toItem: contentView, + attribute: .width, + multiplier: calculatedTitlePercentage, + constant: 0.0)) + } else { + dynamicConstraints += NSLayoutConstraint.constraints(withVisualFormat: "H:[imageView]-(15)-[textField]-|", options: [], metrics: nil, views: views) + } + } else { + if let titleLabel = titleLabel, let text = titleLabel.text, !text.isEmpty { + dynamicConstraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|-[titleLabel]-[textField]-|", options: [], metrics: nil, views: views) + dynamicConstraints.append(NSLayoutConstraint(item: titleLabel, + attribute: .width, + relatedBy: (row as? FieldRowConformance)?.titlePercentage != nil ? .equal : .lessThanOrEqual, + toItem: contentView, + attribute: .width, + multiplier: calculatedTitlePercentage, + constant: 0.0)) + } else { + dynamicConstraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|-[textField]-|", options: .alignAllLeft, metrics: nil, views: views) + } } } contentView.addConstraints(dynamicConstraints) } - - open override func updateConstraints(){ + + open override func updateConstraints() { customConstraints() super.updateConstraints() } - - open func textFieldDidChange(_ textField : UITextField){ - + + @objc open func textFieldDidChange(_ textField: UITextField) { + guard let textValue = textField.text else { row.value = nil return @@ -303,31 +374,36 @@ open class _FieldCell : Cell, UITextFieldDelegate, TextFieldCell where T: textField.selectedTextRange = textField.textRange(from: selStartPos, to: selStartPos) return } - } - else { + } else { let value: AutoreleasingUnsafeMutablePointer = AutoreleasingUnsafeMutablePointer.init(UnsafeMutablePointer.allocate(capacity: 1)) let errorDesc: AutoreleasingUnsafeMutablePointer? = nil if formatter.getObjectValue(value, for: textValue, errorDescription: errorDesc) { row.value = value.pointee as? T - } - else{ + } else { row.value = textValue.isEmpty ? nil : (T.init(string: textValue) ?? row.value) } } } - - //Mark: Helpers - + + // MARK: Helpers + + private func setupTitleLabel() { + titleLabel = self.textLabel + titleLabel?.translatesAutoresizingMaskIntoConstraints = false + titleLabel?.setContentHuggingPriority(UILayoutPriority(rawValue: 500), for: .horizontal) + titleLabel?.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 1000), for: .horizontal) + } + private func displayValue(useFormatter: Bool) -> String? { guard let v = row.value else { return nil } if let formatter = (row as? FormatterConformance)?.formatter, useFormatter { - return textField.isFirstResponder ? formatter.editingString(for: v) : formatter.string(for: v) + return textField?.isFirstResponder == true ? formatter.editingString(for: v) : formatter.string(for: v) } return String(describing: v) } - - //MARK: TextFieldDelegate - + + // MARK: TextFieldDelegate + open func textFieldDidBeginEditing(_ textField: UITextField) { formViewController()?.beginEditing(of: self) formViewController()?.textInputDidBeginEditing(textField, cell: self) @@ -337,33 +413,55 @@ open class _FieldCell : Cell, UITextFieldDelegate, TextFieldCell where T: textField.text = displayValue(useFormatter: false) } } - + open func textFieldDidEndEditing(_ textField: UITextField) { formViewController()?.endEditing(of: self) formViewController()?.textInputDidEndEditing(textField, cell: self) textFieldDidChange(textField) textField.text = displayValue(useFormatter: (row as? FormatterConformance)?.formatter != nil) } - + open func textFieldShouldReturn(_ textField: UITextField) -> Bool { return formViewController()?.textInputShouldReturn(textField, cell: self) ?? true } - + open func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { return formViewController()?.textInput(textField, shouldChangeCharactersInRange:range, replacementString:string, cell: self) ?? true } - + open func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { return formViewController()?.textInputShouldBeginEditing(textField, cell: self) ?? true } - + open func textFieldShouldClear(_ textField: UITextField) -> Bool { return formViewController()?.textInputShouldClear(textField, cell: self) ?? true } - + open func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { return formViewController()?.textInputShouldEndEditing(textField, cell: self) ?? true - } - - + } + + open override func layoutSubviews() { + super.layoutSubviews() + guard let row = (row as? FieldRowConformance) else { return } + defer { + // As titleLabel is the textLabel, iOS may re-layout without updating constraints, for example: + // swiping, showing alert or actionsheet from the same section. + // thus we need forcing update to use customConstraints() + setNeedsUpdateConstraints() + updateConstraintsIfNeeded() + } + guard let titlePercentage = row.titlePercentage else { return } + var targetTitleWidth = bounds.size.width * titlePercentage + if let imageView = imageView, let _ = imageView.image, let titleLabel = titleLabel { + var extraWidthToSubtract = titleLabel.frame.minX - imageView.frame.minX // Left-to-right interface layout + if #available(iOS 9.0, *) { + if UIView.userInterfaceLayoutDirection(for: self.semanticContentAttribute) == .rightToLeft { + extraWidthToSubtract = imageView.frame.maxX - titleLabel.frame.maxX + } + } + targetTitleWidth -= extraWidthToSubtract + } + calculatedTitlePercentage = targetTitleWidth / contentView.bounds.size.width + } } diff --git a/Pods/Eureka/Source/Rows/Common/GenericMultipleSelectorRow.swift b/Pods/Eureka/Source/Rows/Common/GenericMultipleSelectorRow.swift index e2695d4..249c935 100644 --- a/Pods/Eureka/Source/Rows/Common/GenericMultipleSelectorRow.swift +++ b/Pods/Eureka/Source/Rows/Common/GenericMultipleSelectorRow.swift @@ -24,64 +24,62 @@ import Foundation - - /// Generic options selector row that allows multiple selection. -open class GenericMultipleSelectorRow: Row, PresenterRowType, NoValueDisplayTextConformance where Cell: BaseCell, Cell.Value == Set, VCType: UIViewController, VCType.RowValue == Set { +open class GenericMultipleSelectorRow: Row, PresenterRowType, NoValueDisplayTextConformance, OptionsProviderRow + where Cell: BaseCell, Cell.Value == Set { + public typealias PresentedController = MultipleSelectorViewController> + /// Defines how the view controller will be presented, pushed, etc. - open var presentationMode: PresentationMode? - + open var presentationMode: PresentationMode? + /// Will be called before the presentation occurs. - open var onPresentCallback : ((FormViewController, VCType)->())? - + open var onPresentCallback: ((FormViewController, PresentedController) -> Void)? + /// Title to be displayed for the options open var selectorTitle: String? - open var noValueDisplayText: String? - + /// Options from which the user will choose - open var options: [T] { - get { return self.dataProvider?.arrayData?.map({ $0.first! }) ?? [] } - set { self.dataProvider = DataProvider(arrayData: newValue.map({ Set(arrayLiteral: $0) })) } - } - + open var optionsProvider: OptionsProvider? + required public init(tag: String?) { super.init(tag: tag) displayValueFor = { (rowValue: Set?) in return rowValue?.map({ String(describing: $0) }).sorted().joined(separator: ", ") } - presentationMode = .show(controllerProvider: ControllerProvider.callback { return VCType() }, onDismiss: { vc in - let _ = vc.navigationController?.popViewController(animated: true) }) + presentationMode = .show(controllerProvider: ControllerProvider.callback { + return MultipleSelectorViewController>() + }, onDismiss: { vc in + let _ = vc.navigationController?.popViewController(animated: true) + }) } - + /** Extends `didSelect` method */ open override func customDidSelect() { super.customDidSelect() guard let presentationMode = presentationMode, !isDisabled else { return } - if let controller = presentationMode.makeController(){ + if let controller = presentationMode.makeController() { controller.row = self controller.title = selectorTitle ?? controller.title onPresentCallback?(cell.formViewController()!, controller) presentationMode.present(controller, row: self, presentingController: self.cell.formViewController()!) - } - else{ + } else { presentationMode.present(nil, row: self, presentingController: self.cell.formViewController()!) } } - + /** Prepares the pushed row setting its title and completion callback. */ open override func prepare(for segue: UIStoryboardSegue) { super.prepare(for: segue) - guard let rowVC = segue.destination as? VCType else { return } + guard let rowVC = segue.destination as Any as? PresentedController else { return } rowVC.title = selectorTitle ?? rowVC.title rowVC.onDismissCallback = presentationMode?.onDismissCallback ?? rowVC.onDismissCallback onPresentCallback?(cell.formViewController()!, rowVC) rowVC.row = self } } - diff --git a/Pods/Eureka/Source/Rows/Common/OptionsRow.swift b/Pods/Eureka/Source/Rows/Common/OptionsRow.swift index a8ae570..b57b040 100644 --- a/Pods/Eureka/Source/Rows/Common/OptionsRow.swift +++ b/Pods/Eureka/Source/Rows/Common/OptionsRow.swift @@ -24,15 +24,13 @@ import Foundation -open class OptionsRow : Row, NoValueDisplayTextConformance where Cell: BaseCell { +open class OptionsRow : Row, NoValueDisplayTextConformance, OptionsProviderRow where Cell: BaseCell { + + open var optionsProvider: OptionsProvider? - open var options: [Cell.Value] { - get { return dataProvider?.arrayData ?? [] } - set { dataProvider = DataProvider(arrayData: newValue) } - } open var selectorTitle: String? open var noValueDisplayText: String? - + required public init(tag: String?) { super.init(tag: tag) } diff --git a/Pods/Eureka/Source/Rows/Common/SelectorRow.swift b/Pods/Eureka/Source/Rows/Common/SelectorRow.swift index 464a80e..d9e5522 100644 --- a/Pods/Eureka/Source/Rows/Common/SelectorRow.swift +++ b/Pods/Eureka/Source/Rows/Common/SelectorRow.swift @@ -25,15 +25,15 @@ import Foundation open class PushSelectorCell : Cell, CellType { - + required public init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) } - + required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } - + open override func update() { super.update() accessoryType = .disclosureIndicator @@ -43,41 +43,41 @@ open class PushSelectorCell : Cell, CellType { } /// Generic row type where a user must select a value among several options. -open class SelectorRow: OptionsRow, PresenterRowType where Cell: BaseCell, VCType: UIViewController, VCType.RowValue == Cell.Value { +open class SelectorRow: OptionsRow, PresenterRowType where Cell: BaseCell { + /// Defines how the view controller will be presented, pushed, etc. - open var presentationMode: PresentationMode? - + open var presentationMode: PresentationMode>>? + /// Will be called before the presentation occurs. - open var onPresentCallback : ((FormViewController, VCType)->())? - + open var onPresentCallback: ((FormViewController, SelectorViewController>) -> Void)? + required public init(tag: String?) { super.init(tag: tag) } - + /** Extends `didSelect` method */ open override func customDidSelect() { super.customDidSelect() guard let presentationMode = presentationMode, !isDisabled else { return } - if let controller = presentationMode.makeController(){ + if let controller = presentationMode.makeController() { controller.row = self controller.title = selectorTitle ?? controller.title onPresentCallback?(cell.formViewController()!, controller) presentationMode.present(controller, row: self, presentingController: self.cell.formViewController()!) - } - else{ + } else { presentationMode.present(nil, row: self, presentingController: self.cell.formViewController()!) } } - + /** Prepares the pushed row setting its title and completion callback. */ open override func prepare(for segue: UIStoryboardSegue) { super.prepare(for: segue) - guard let rowVC = segue.destination as? VCType else { return } + guard let rowVC = segue.destination as Any as? SelectorViewController> else { return } rowVC.title = selectorTitle ?? rowVC.title rowVC.onDismissCallback = presentationMode?.onDismissCallback ?? rowVC.onDismissCallback onPresentCallback?(cell.formViewController()!, rowVC) diff --git a/Pods/Eureka/Source/Rows/Controllers/MultipleSelectorViewController.swift b/Pods/Eureka/Source/Rows/Controllers/MultipleSelectorViewController.swift index a1df1f2..bcd834c 100644 --- a/Pods/Eureka/Source/Rows/Controllers/MultipleSelectorViewController.swift +++ b/Pods/Eureka/Source/Rows/Controllers/MultipleSelectorViewController.swift @@ -24,102 +24,125 @@ import Foundation - /// Selector Controller that enables multiple selection -open class _MultipleSelectorViewController : FormViewController, TypedRowControllerType where Row: BaseRow, Row: TypedRowType, Row.Cell.Value == T { - +open class _MultipleSelectorViewController : FormViewController, TypedRowControllerType where Row: BaseRow, Row.Cell.Value == OptionsRow.OptionsProviderType.Option, OptionsRow.OptionsProviderType.Option: Hashable { + /// The row that pushed or presented this controller - public var row: RowOf>! - - public var selectableRowCellSetup: ((_ cell: Row.Cell, _ row: Row) -> ())? - public var selectableRowCellUpdate: ((_ cell: Row.Cell, _ row: Row) -> ())? + public var row: RowOf>! - /// A closure to be called when the controller disappears. - public var onDismissCallback : ((UIViewController) -> ())? + public var selectableRowSetup: ((_ row: Row) -> Void)? + public var selectableRowCellSetup: ((_ cell: Row.Cell, _ row: Row) -> Void)? + public var selectableRowCellUpdate: ((_ cell: Row.Cell, _ row: Row) -> Void)? + /// A closure to be called when the controller disappears. + public var onDismissCallback: ((UIViewController) -> Void)? + /// A closure that should return key for particular row value. /// This key is later used to break options by sections. public var sectionKeyForValue: ((Row.Cell.Value) -> (String))? - + /// A closure that returns header title for a section for particular key. /// By default returns the key itself. public var sectionHeaderTitleForKey: ((String) -> String?)? = { $0 } - + /// A closure that returns footer title for a section for particular key. public var sectionFooterTitleForKey: ((String) -> String?)? + + + public var optionsProviderRow: OptionsRow { + return row as! OptionsRow + } + override public init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) } - - convenience public init(_ callback: ((UIViewController) -> ())?){ + + convenience public init(_ callback: ((UIViewController) -> Void)?) { self.init(nibName: nil, bundle: nil) onDismissCallback = callback } - + public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } - + open override func viewDidLoad() { super.viewDidLoad() setupForm() } - - open func setupForm() { - guard let options = row.dataProvider?.arrayData else { return } - if let optionsBySections = self.optionsBySections() { + open func setupForm() { + optionsProviderRow.optionsProvider?.options(for: self) { [weak self] (options: [OptionsRow.OptionsProviderType.Option]?) in + guard let strongSelf = self, let options = options else { return } + strongSelf.optionsProviderRow.cachedOptionsData = options + strongSelf.setupForm(with: options) + } + } + + open func setupForm(with options: [OptionsRow.OptionsProviderType.Option]) { + if let optionsBySections = optionsBySections(with: options) { for (sectionKey, options) in optionsBySections { - form +++ section(with: options, header: sectionHeaderTitleForKey?(sectionKey), footer: sectionFooterTitleForKey?(sectionKey)) + form +++ section(with: options, + header: sectionHeaderTitleForKey?(sectionKey), + footer: sectionFooterTitleForKey?(sectionKey)) } } else { form +++ section(with: options, header: row.title, footer: nil) } } + + open func optionsBySections(with options: [OptionsRow.OptionsProviderType.Option]) -> [(String, [Row.Cell.Value])]? { + guard let sectionKeyForValue = sectionKeyForValue else { return nil } - open func optionsBySections() -> [(String, [Set])]? { - guard let options = row.dataProvider?.arrayData, let sectionKeyForValue = sectionKeyForValue else { return nil } - - let sections = options.reduce([:]) { (reduced, option) -> [String: [Set]] in + let sections = options.reduce([:]) { (reduced, option) -> [String: [Row.Cell.Value]] in var reduced = reduced - let key = sectionKeyForValue(option.first!) - reduced[key] = (reduced[key] ?? []) + [option] + let key = sectionKeyForValue(option) + var items = reduced[key] ?? [] + items.append(option) + reduced[key] = items return reduced } - + return sections.sorted(by: { (lhs, rhs) in lhs.0 < rhs.0 }) } - - func section(with options: [Set], header: String?, footer: String?) -> SelectableSection { - let section = SelectableSection(header: header ?? "", footer: footer ?? "", selectionType: .multipleSelection) { [weak self] section in - if let sec = section as? SelectableSection { - sec.onSelectSelectableRow = { _, selectableRow in - var newValue: Set = self?.row.value ?? [] - if let selectableValue = selectableRow.value { - newValue.insert(selectableValue) - } - else { - newValue.remove(selectableRow.selectableValue!) - } - self?.row.value = newValue + + func section(with options: [OptionsRow.OptionsProviderType.Option], header: String?, footer: String?) -> SelectableSection { + let section = SelectableSection(header: header ?? "", footer: footer ?? "", selectionType: .multipleSelection) { section in + section.onSelectSelectableRow = { [weak self] _, selectableRow in + var newValue: Set = self?.row.value ?? [] + if let selectableValue = selectableRow.value { + newValue.insert(selectableValue) + } else { + newValue.remove(selectableRow.selectableValue!) } + self?.row.value = newValue } } for option in options { - section <<< Row.init() { lrow in - lrow.title = String(describing: option.first!) - lrow.selectableValue = option.first! - lrow.value = self.row.value?.contains(option.first!) ?? false ? option.first! : nil - }.cellSetup { [weak self] cell, row in - self?.selectableRowCellSetup?(cell, row) - }.cellUpdate { [weak self] cell, row in - self?.selectableRowCellUpdate?(cell, row) + section <<< Row.init { lrow in + lrow.title = String(describing: option) + lrow.selectableValue = option + lrow.value = self.row.value?.contains(option) ?? false ? option : nil + self.selectableRowSetup?(lrow) + }.cellSetup { [weak self] cell, row in + self?.selectableRowCellSetup?(cell, row) + }.cellUpdate { [weak self] cell, row in + self?.selectableRowCellUpdate?(cell, row) } } return section } } -open class MultipleSelectorViewController : _MultipleSelectorViewController> { +open class MultipleSelectorViewController: _MultipleSelectorViewController, OptionsRow> where OptionsRow.OptionsProviderType.Option: Hashable{ + + override public init(nibName nibNameOrNil: String? = nil, bundle nibBundleOrNil: Bundle? = nil) { + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + } diff --git a/Pods/Eureka/Source/Rows/Controllers/SelectorAlertController.swift b/Pods/Eureka/Source/Rows/Controllers/SelectorAlertController.swift index 11cab17..e73a4c0 100644 --- a/Pods/Eureka/Source/Rows/Controllers/SelectorAlertController.swift +++ b/Pods/Eureka/Source/Rows/Controllers/SelectorAlertController.swift @@ -24,40 +24,56 @@ import Foundation +/// Specific type, Responsible for the options passed to a selector alert view controller +public protocol AlertOptionsProviderRow: OptionsProviderRow { + + var cancelTitle: String? { get set } + +} + /// Selector UIAlertController -open class SelectorAlertController : UIAlertController, TypedRowControllerType { - +open class SelectorAlertController: UIAlertController, TypedRowControllerType where AlertOptionsRow.OptionsProviderType.Option == AlertOptionsRow.Cell.Value, AlertOptionsRow: BaseRow { + /// The row that pushed or presented this controller - public var row: RowOf! - + public var row: RowOf! + + @available(*, deprecated, message: "Use AlertOptionsRow.cancelTitle instead.") public var cancelTitle = NSLocalizedString("Cancel", comment: "") - + /// A closure to be called when the controller disappears. - public var onDismissCallback : ((UIViewController) -> ())? + public var onDismissCallback: ((UIViewController) -> Void)? + /// Options provider to use to get available options. + /// If not set will use synchronous data provider built with `row.dataProvider.arrayData`. + // public var optionsProvider: OptionsProvider? + public var optionsProviderRow: AlertOptionsRow { + return row as Any as! AlertOptionsRow + } + override public init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) } - + required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } - - convenience public init(_ callback: ((UIViewController) -> ())?){ + + convenience public init(_ callback: ((UIViewController) -> Void)?) { self.init() onDismissCallback = callback } - + open override func viewDidLoad() { super.viewDidLoad() - guard let options = row.dataProvider?.arrayData else { return } + guard let options = optionsProviderRow.options else { return } + let cancelTitle = optionsProviderRow.cancelTitle ?? NSLocalizedString("Cancel", comment: "") addAction(UIAlertAction(title: cancelTitle, style: .cancel, handler: nil)) for option in options { addAction(UIAlertAction(title: row.displayValueFor?(option), style: .default, handler: { [weak self] _ in self?.row.value = option self?.onDismissCallback?(self!) - })) + })) } } - + } diff --git a/Pods/Eureka/Source/Rows/Controllers/SelectorViewController.swift b/Pods/Eureka/Source/Rows/Controllers/SelectorViewController.swift index a4010d3..47f984a 100644 --- a/Pods/Eureka/Source/Rows/Controllers/SelectorViewController.swift +++ b/Pods/Eureka/Source/Rows/Controllers/SelectorViewController.swift @@ -24,36 +24,124 @@ import Foundation -open class _SelectorViewController: FormViewController, TypedRowControllerType where Row: BaseRow, Row: TypedRowType { + +/** + * Responsible for the options passed to a selector view controller + */ + +public protocol OptionsProviderRow: TypedRowType { + associatedtype OptionsProviderType: OptionsProviderConformance + + var optionsProvider: OptionsProviderType? { get set } + + var cachedOptionsData: [OptionsProviderType.Option]? { get set } +} + +extension OptionsProviderRow where Self: BaseRow { + + public var options: [OptionsProviderType.Option]? { + set (newValue){ + let optProvider = OptionsProviderType.init(array: newValue) + optionsProvider = optProvider + } + get { + return self.cachedOptionsData ?? optionsProvider?.optionsArray + } + } + + public var cachedOptionsData: [OptionsProviderType.Option]? { + get { + return self._cachedOptionsData as? [OptionsProviderType.Option] + } + set { + self._cachedOptionsData = newValue + } + } +} + +public protocol OptionsProviderConformance: ExpressibleByArrayLiteral { + associatedtype Option: Equatable + init(array: [Option]?) + func options(for selectorViewController: FormViewController, completion: @escaping ([Option]?) -> Void) + var optionsArray: [Option]? { get } + +} + +/// Provider of selectable options. +public enum OptionsProvider: OptionsProviderConformance { + + /// Synchronous provider that provides array of options it was initialized with + case array([T]?) + /// Provider that uses closure it was initialized with to provide options. Can be synchronous or asynchronous. + case lazy((FormViewController, @escaping ([T]?) -> Void) -> Void) + + public init(array: [T]?) { + self = .array(array) + } + + public init(arrayLiteral elements: T...) { + self = .array(elements) + } + + public func options(for selectorViewController: FormViewController, completion: @escaping ([T]?) -> Void) { + switch self { + case let .array(array): + completion(array) + case let .lazy(fetch): + fetch(selectorViewController, completion) + } + } + + public var optionsArray: [T]?{ + switch self { + case let .array(arrayData): + return arrayData + default: + return nil + } + } +} + +open class _SelectorViewController: FormViewController, TypedRowControllerType where Row: BaseRow, Row.Cell.Value == OptionsRow.OptionsProviderType.Option { + /// The row that pushed or presented this controller public var row: RowOf! public var enableDeselection = true public var dismissOnSelection = true public var dismissOnChange = true - - public var selectableRowCellUpdate: ((_ cell: Row.Cell, _ row: Row) -> ())? - public var selectableRowCellSetup: ((_ cell: Row.Cell, _ row: Row) -> ())? - + + public var selectableRowSetup: ((_ row: Row) -> Void)? + public var selectableRowCellUpdate: ((_ cell: Row.Cell, _ row: Row) -> Void)? + public var selectableRowCellSetup: ((_ cell: Row.Cell, _ row: Row) -> Void)? + /// A closure to be called when the controller disappears. - public var onDismissCallback : ((UIViewController) -> ())? - + public var onDismissCallback: ((UIViewController) -> Void)? + /// A closure that should return key for particular row value. /// This key is later used to break options by sections. public var sectionKeyForValue: ((Row.Cell.Value) -> (String))? - + /// A closure that returns header title for a section for particular key. /// By default returns the key itself. public var sectionHeaderTitleForKey: ((String) -> String?)? = { $0 } - + /// A closure that returns footer title for a section for particular key. public var sectionFooterTitleForKey: ((String) -> String?)? + + public var optionsProviderRow: OptionsRow { + return row as! OptionsRow + } + + override public init(style: UITableViewStyle) { + super.init(style: style) + } override public init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) } - - convenience public init(_ callback: ((UIViewController) -> ())?) { + + convenience public init(_ callback: ((UIViewController) -> Void)?) { self.init(nibName: nil, bundle: nil) onDismissCallback = callback } @@ -61,67 +149,86 @@ open class _SelectorViewController: FormViewController, public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } - + open override func viewDidLoad() { super.viewDidLoad() setupForm() } - + open func setupForm() { - guard let options = row.dataProvider?.arrayData else { return } - - if let optionsBySections = self.optionsBySections() { + let optProvider = optionsProviderRow.optionsProvider + optProvider?.options(for: self) { [weak self] (options: [Row.Cell.Value]?) in + guard let strongSelf = self, let options = options else { return } + strongSelf.optionsProviderRow.cachedOptionsData = options + strongSelf.setupForm(with: options) + } + } + + open func setupForm(with options: [Row.Cell.Value]) { + if let optionsBySections = optionsBySections(with: options) { for (sectionKey, options) in optionsBySections { - form +++ section(with: options, header: sectionHeaderTitleForKey?(sectionKey), footer: sectionFooterTitleForKey?(sectionKey)) + form +++ section(with: options, + header: sectionHeaderTitleForKey?(sectionKey), + footer: sectionFooterTitleForKey?(sectionKey)) } } else { form +++ section(with: options, header: row.title, footer: nil) } } - func optionsBySections() -> [(String, [Row.Cell.Value])]? { - guard let options = row.dataProvider?.arrayData, let sectionKeyForValue = sectionKeyForValue else { return nil } - + func optionsBySections(with options: [Row.Cell.Value]) -> [(String, [Row.Cell.Value])]? { + guard let sectionKeyForValue = sectionKeyForValue else { return nil } + let sections = options.reduce([:]) { (reduced, option) -> [String: [Row.Cell.Value]] in var reduced = reduced let key = sectionKeyForValue(option) reduced[key] = (reduced[key] ?? []) + [option] return reduced } - + return sections.sorted(by: { (lhs, rhs) in lhs.0 < rhs.0 }) } - + func section(with options: [Row.Cell.Value], header: String?, footer: String?) -> SelectableSection { let header = header ?? "" let footer = footer ?? "" - let section = SelectableSection(header: header, footer: footer, selectionType: .singleSelection(enableDeselection: enableDeselection)) { [weak self] section in - if let sec = section as? SelectableSection { - sec.onSelectSelectableRow = { _, row in - let changed = self?.row.value != row.value - self?.row.value = row.value - if self?.dismissOnSelection == true || (changed && self?.dismissOnChange == true) { - self?.onDismissCallback?(self!) + let section = SelectableSection(header: header, footer: footer, selectionType: .singleSelection(enableDeselection: enableDeselection)) { section in + section.onSelectSelectableRow = { [weak self] _, row in + let changed = self?.row.value != row.value + self?.row.value = row.value + + if let form = row.section?.form { + for section in form where section !== row.section { + let section = section as Any as! SelectableSection + if let selectedRow = section.selectedRow(), selectedRow !== row { + selectedRow.value = nil + selectedRow.updateCell() + } } } + + if self?.dismissOnSelection == true || (changed && self?.dismissOnChange == true) { + self?.onDismissCallback?(self!) + } } } for option in options { - section <<< Row.init(String(describing: option)){ lrow in + section <<< Row.init(String(describing: option)) { lrow in lrow.title = self.row.displayValueFor?(option) lrow.selectableValue = option lrow.value = self.row.value == option ? option : nil - }.cellSetup { [weak self] cell, row in - self?.selectableRowCellSetup?(cell, row) - }.cellUpdate { [weak self] cell, row in - self?.selectableRowCellUpdate?(cell, row) + self.selectableRowSetup?(lrow) + }.cellSetup { [weak self] cell, row in + self?.selectableRowCellSetup?(cell, row) + }.cellUpdate { [weak self] cell, row in + self?.selectableRowCellUpdate?(cell, row) } } return section } - + } /// Selector Controller (used to select one option among a list) -open class SelectorViewController : _SelectorViewController> { +open class SelectorViewController: _SelectorViewController, OptionsRow> { } diff --git a/Pods/Eureka/Source/Rows/DateInlineRow.swift b/Pods/Eureka/Source/Rows/DateInlineRow.swift index 6d49229..959b724 100644 --- a/Pods/Eureka/Source/Rows/DateInlineRow.swift +++ b/Pods/Eureka/Source/Rows/DateInlineRow.swift @@ -24,82 +24,77 @@ import Foundation - extension DatePickerRowProtocol { - - func configureInlineRow(_ inlineRow: DatePickerRowProtocol){ + + func configureInlineRow(_ inlineRow: DatePickerRowProtocol) { inlineRow.minimumDate = minimumDate inlineRow.maximumDate = maximumDate inlineRow.minuteInterval = minuteInterval } - -} +} open class _DateInlineRow: _DateInlineFieldRow { - + public typealias InlineRow = DatePickerRow - + public required init(tag: String?) { super.init(tag: tag) dateFormatter?.timeStyle = .none dateFormatter?.dateStyle = .medium } - + open func setupInlineRow(_ inlineRow: DatePickerRow) { configureInlineRow(inlineRow) } } open class _TimeInlineRow: _DateInlineFieldRow { - + public typealias InlineRow = TimePickerRow - + public required init(tag: String?) { super.init(tag: tag) dateFormatter?.timeStyle = .short dateFormatter?.dateStyle = .none } - + open func setupInlineRow(_ inlineRow: TimePickerRow) { configureInlineRow(inlineRow) } } open class _DateTimeInlineRow: _DateInlineFieldRow { - + public typealias InlineRow = DateTimePickerRow - + public required init(tag: String?) { super.init(tag: tag) dateFormatter?.timeStyle = .short dateFormatter?.dateStyle = .short } - + open func setupInlineRow(_ inlineRow: DateTimePickerRow) { configureInlineRow(inlineRow) } } open class _CountDownInlineRow: _DateInlineFieldRow { - + public typealias InlineRow = CountDownPickerRow - + public required init(tag: String?) { super.init(tag: tag) - displayValueFor = { + displayValueFor = { guard let date = $0 else { return nil } - let hour = Calendar.current.component(.hour, from: date) - let min = Calendar.current.component(.minute, from: date) - if hour == 1{ - return "\(hour) hour \(min) min" - } - return "\(hour) hours \(min) min" + + let dateComponents = Calendar.current.dateComponents([.hour, .minute], from: date) + return DateComponentsFormatter.localizedString(from: dateComponents, unitsStyle: .full)?.replacingOccurrences(of: ",", with: "") } } - + public func setupInlineRow(_ inlineRow: CountDownPickerRow) { configureInlineRow(inlineRow) } @@ -117,7 +112,7 @@ public final class DateInlineRow_: _DateInlineRow, RowType, InlineRowType { cell.detailTextLabel?.textColor = cell.tintColor } } - + public override func customDidSelect() { super.customDidSelect() if !isDisabled { @@ -128,7 +123,6 @@ public final class DateInlineRow_: _DateInlineRow, RowType, InlineRowType { public typealias DateInlineRow = DateInlineRow_ - /// A row with an Date as value where the user can select date and time from an inline picker view. public final class DateTimeInlineRow_: _DateTimeInlineRow, RowType, InlineRowType { required public init(tag: String?) { @@ -141,7 +135,7 @@ public final class DateTimeInlineRow_: _DateTimeInlineRow, RowType, InlineRow cell.detailTextLabel?.textColor = cell.tintColor } } - + public override func customDidSelect() { super.customDidSelect() if !isDisabled { @@ -150,10 +144,8 @@ public final class DateTimeInlineRow_: _DateTimeInlineRow, RowType, InlineRow } } - public typealias DateTimeInlineRow = DateTimeInlineRow_ - /// A row with an Date as value where the user can select a time from an inline picker view. public final class TimeInlineRow_: _TimeInlineRow, RowType, InlineRowType { required public init(tag: String?) { @@ -166,7 +158,7 @@ public final class TimeInlineRow_: _TimeInlineRow, RowType, InlineRowType { cell.detailTextLabel?.textColor = cell.tintColor } } - + public override func customDidSelect() { super.customDidSelect() if !isDisabled { @@ -189,7 +181,7 @@ public final class CountDownInlineRow_: _CountDownInlineRow, RowType, InlineR cell.detailTextLabel?.textColor = cell.tintColor } } - + public override func customDidSelect() { super.customDidSelect() if !isDisabled { @@ -199,5 +191,3 @@ public final class CountDownInlineRow_: _CountDownInlineRow, RowType, InlineR } public typealias CountDownInlineRow = CountDownInlineRow_ - - diff --git a/Pods/Eureka/Source/Rows/DatePickerRow.swift b/Pods/Eureka/Source/Rows/DatePickerRow.swift index 9edf1c8..9204f25 100644 --- a/Pods/Eureka/Source/Rows/DatePickerRow.swift +++ b/Pods/Eureka/Source/Rows/DatePickerRow.swift @@ -24,37 +24,40 @@ import Foundation -open class DatePickerCell : Cell, CellType { - - public var datePicker: UIDatePicker - - public required init(style: UITableViewCellStyle, reuseIdentifier: String?){ - self.datePicker = UIDatePicker() +open class DatePickerCell: Cell, CellType { + + @IBOutlet weak public var datePicker: UIDatePicker! + + public required init(style: UITableViewCellStyle, reuseIdentifier: String?) { + let datePicker = UIDatePicker() + self.datePicker = datePicker self.datePicker.translatesAutoresizingMaskIntoConstraints = false - + super.init(style: style, reuseIdentifier: reuseIdentifier) - + self.contentView.addSubview(self.datePicker) self.contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[picker]-0-|", options: [], metrics: nil, views: ["picker": self.datePicker])) self.contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[picker]-0-|", options: [], metrics: nil, views: ["picker": self.datePicker])) - self.datePicker.addTarget(self, action: #selector(DatePickerCell.datePickerValueChanged(_:)), for: .valueChanged) } - + required public init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") + super.init(coder: aDecoder) } - + open override func setup() { super.setup() + selectionStyle = .none accessoryType = .none editingAccessoryType = .none + height = { UITableViewAutomaticDimension } datePicker.datePickerMode = datePickerMode() + datePicker.addTarget(self, action: #selector(DatePickerCell.datePickerValueChanged(_:)), for: .valueChanged) } - + deinit { - datePicker.removeTarget(self, action: nil, for: .allEvents) + datePicker?.removeTarget(self, action: nil, for: .allEvents) } - + open override func update() { super.update() selectionStyle = row.isDisabled ? .none : .default @@ -64,16 +67,23 @@ open class DatePickerCell : Cell, CellType { datePicker.setDate(row.value ?? Date(), animated: row is CountDownPickerRow) datePicker.minimumDate = (row as? DatePickerRowProtocol)?.minimumDate datePicker.maximumDate = (row as? DatePickerRowProtocol)?.maximumDate - if let minuteIntervalValue = (row as? DatePickerRowProtocol)?.minuteInterval{ + if let minuteIntervalValue = (row as? DatePickerRowProtocol)?.minuteInterval { datePicker.minuteInterval = minuteIntervalValue } } - - func datePickerValueChanged(_ sender: UIDatePicker){ + + @objc func datePickerValueChanged(_ sender: UIDatePicker) { row?.value = sender.date + + // workaround for UIDatePicker bug when it doesn't trigger "value changed" event after trying to pick 00:00 value + // for details see this comment: https://stackoverflow.com/questions/20181980/uidatepicker-bug-uicontroleventvaluechanged-after-hitting-minimum-internal#comment56681891_20204225 + if sender.datePickerMode == .countDownTimer && sender.countDownDuration == TimeInterval(sender.minuteInterval * 60) { + datePicker.countDownDuration = sender.countDownDuration + } + } - - private func datePickerMode() -> UIDatePickerMode{ + + private func datePickerMode() -> UIDatePickerMode { switch row { case is DatePickerRow: return .date @@ -87,14 +97,15 @@ open class DatePickerCell : Cell, CellType { return .date } } + } -open class _DatePickerRow : Row, DatePickerRowProtocol { - - open var minimumDate : Date? - open var maximumDate : Date? - open var minuteInterval : Int? - +open class _DatePickerRow: Row, DatePickerRowProtocol { + + open var minimumDate: Date? + open var maximumDate: Date? + open var minuteInterval: Int? + required public init(tag: String?) { super.init(tag: tag) displayValueFor = nil @@ -102,28 +113,28 @@ open class _DatePickerRow : Row, DatePickerRowProtocol { } /// A row with an Date as value where the user can select a date directly. -public final class DatePickerRow : _DatePickerRow, RowType { +public final class DatePickerRow: _DatePickerRow, RowType { public required init(tag: String?) { super.init(tag: tag) } } /// A row with an Date as value where the user can select a time directly. -public final class TimePickerRow : _DatePickerRow, RowType { +public final class TimePickerRow: _DatePickerRow, RowType { public required init(tag: String?) { super.init(tag: tag) } } /// A row with an Date as value where the user can select date and time directly. -public final class DateTimePickerRow : _DatePickerRow, RowType { +public final class DateTimePickerRow: _DatePickerRow, RowType { public required init(tag: String?) { super.init(tag: tag) } } /// A row with an Date as value where the user can select hour and minute as a countdown timer. -public final class CountDownPickerRow : _DatePickerRow, RowType { +public final class CountDownPickerRow: _DatePickerRow, RowType { public required init(tag: String?) { super.init(tag: tag) } diff --git a/Pods/Eureka/Source/Rows/DateRow.swift b/Pods/Eureka/Source/Rows/DateRow.swift index f08ecbd..efb9e6b 100644 --- a/Pods/Eureka/Source/Rows/DateRow.swift +++ b/Pods/Eureka/Source/Rows/DateRow.swift @@ -33,7 +33,6 @@ open class _DateRow: _DateFieldRow { } } - open class _TimeRow: _DateFieldRow { required public init(tag: String?) { super.init(tag: tag) @@ -64,12 +63,9 @@ open class _CountDownRow: _DateFieldRow { if let formatter = self.dateFormatter { return formatter.string(from: val) } - let components = Calendar.current.dateComponents([Calendar.Component.minute, Calendar.Component.hour], from: val) - var hourString = "hour" - if components.hour != 1{ - hourString += "s" - } - return "\(components.hour!) \(hourString) \(components.minute!) min" + + let dateComponents = Calendar.current.dateComponents([.hour, .minute], from: val) + return DateComponentsFormatter.localizedString(from: dateComponents, unitsStyle: .full)?.replacingOccurrences(of: ",", with: "") } } } @@ -81,7 +77,6 @@ public final class DateRow: _DateRow, RowType { } } - /// A row with an Date as value where the user can select a time from a picker view. public final class TimeRow: _TimeRow, RowType { required public init(tag: String?) { @@ -102,6 +97,3 @@ public final class CountDownRow: _CountDownRow, RowType { super.init(tag: tag) } } - - - diff --git a/Pods/Eureka/Source/Rows/DoublePickerInputRow.swift b/Pods/Eureka/Source/Rows/DoublePickerInputRow.swift new file mode 100644 index 0000000..b88ee78 --- /dev/null +++ b/Pods/Eureka/Source/Rows/DoublePickerInputRow.swift @@ -0,0 +1,111 @@ +// +// DoublePickerInputRow.swift +// Eureka +// +// Created by Mathias Claassen on 5/10/18. +// Copyright © 2018 Xmartlabs. All rights reserved. +// + +import Foundation + +open class DoublePickerInputCell : _PickerInputCell> where A: Equatable, B: Equatable { + + private var pickerRow: _DoublePickerInputRow! { return row as? _DoublePickerInputRow } + + public required init(style: UITableViewCellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + open override func update() { + super.update() + if let selectedValue = pickerRow.value, let indexA = pickerRow.firstOptions().index(of: selectedValue.a), + let indexB = pickerRow.secondOptions(selectedValue.a).index(of: selectedValue.b) { + picker.selectRow(indexA, inComponent: 0, animated: true) + picker.selectRow(indexB, inComponent: 1, animated: true) + } + } + + open override func numberOfComponents(in pickerView: UIPickerView) -> Int { + return 2 + } + + open override func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { + return component == 0 ? pickerRow.firstOptions().count : pickerRow.secondOptions(pickerRow.selectedFirst()).count + } + + open override func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { + if component == 0 { + return pickerRow.displayValueForFirstRow(pickerRow.firstOptions()[row]) + } else { + return pickerRow.displayValueForSecondRow(pickerRow.secondOptions(pickerRow.selectedFirst())[row]) + } + } + + open override func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { + if component == 0 { + let a = pickerRow.firstOptions()[row] + if let value = pickerRow.value { + guard value.a != a else { + return + } + if pickerRow.secondOptions(a).contains(value.b) { + pickerRow.value = Tuple(a: a, b: value.b) + pickerView.reloadComponent(1) + update() + return + } else { + pickerRow.value = Tuple(a: a, b: pickerRow.secondOptions(a)[0]) + } + } else { + pickerRow.value = Tuple(a: a, b: pickerRow.secondOptions(a)[0]) + } + pickerView.reloadComponent(1) + pickerView.selectRow(0, inComponent: 1, animated: true) + } else { + let a = pickerRow.selectedFirst() + pickerRow.value = Tuple(a: a, b: pickerRow.secondOptions(a)[row]) + } + update() + } +} + +open class _DoublePickerInputRow : Row>, NoValueDisplayTextConformance { + + open var noValueDisplayText: String? = nil + /// Options for first component. Will be called often so should be O(1) + public var firstOptions: (() -> [A]) = {[]} + /// Options for second component given the selected value from the first component. Will be called often so should be O(1) + public var secondOptions: ((A) -> [B]) = {_ in []} + + /// Modify the displayed values for the first picker row. + public var displayValueForFirstRow: ((A) -> (String)) = { a in return String(describing: a) } + /// Modify the displayed values for the second picker row. + public var displayValueForSecondRow: ((B) -> (String)) = { b in return String(describing: b) } + + required public init(tag: String?) { + super.init(tag: tag) + } + + func selectedFirst() -> A { + return value?.a ?? firstOptions()[0] + } + +} + +/// A generic row where the user can pick an option from a picker view displayed in the keyboard area +public final class DoublePickerInputRow: _DoublePickerInputRow, RowType where A: Equatable, B: Equatable { + + required public init(tag: String?) { + super.init(tag: tag) + self.displayValueFor = { [weak self] tuple in + guard let tuple = tuple else { + return self?.noValueDisplayText + } + return String(describing: tuple.a) + ", " + String(describing: tuple.b) + } + } +} diff --git a/Pods/Eureka/Source/Rows/DoublePickerRow.swift b/Pods/Eureka/Source/Rows/DoublePickerRow.swift new file mode 100644 index 0000000..7e5bcc9 --- /dev/null +++ b/Pods/Eureka/Source/Rows/DoublePickerRow.swift @@ -0,0 +1,125 @@ +// +// MultiplePickerRow.swift +// Eureka +// +// Created by Mathias Claassen on 5/8/18. +// Copyright © 2018 Xmartlabs. All rights reserved. +// + +import Foundation + +public struct Tuple { + + public let a: A + public let b: B + + public init(a: A, b: B) { + self.a = a + self.b = b + } + +} + +extension Tuple: Equatable {} + +public func == (lhs: Tuple, rhs: Tuple) -> Bool { + return lhs.a == rhs.a && lhs.b == rhs.b +} + +// MARK: MultiplePickerCell + +open class DoublePickerCell : _PickerCell> where A: Equatable, B: Equatable { + + private var pickerRow: _DoublePickerRow! { return row as? _DoublePickerRow } + + public required init(style: UITableViewCellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + open override func update() { + super.update() + if let selectedValue = pickerRow.value, let indexA = pickerRow.firstOptions().index(of: selectedValue.a), + let indexB = pickerRow.secondOptions(selectedValue.a).index(of: selectedValue.b) { + picker.selectRow(indexA, inComponent: 0, animated: true) + picker.selectRow(indexB, inComponent: 1, animated: true) + } + } + + open override func numberOfComponents(in pickerView: UIPickerView) -> Int { + return 2 + } + + open override func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { + return component == 0 ? pickerRow.firstOptions().count : pickerRow.secondOptions(pickerRow.selectedFirst()).count + } + + open override func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { + if component == 0 { + return pickerRow.displayValueForFirstRow(pickerRow.firstOptions()[row]) + } else { + return pickerRow.displayValueForSecondRow(pickerRow.secondOptions(pickerRow.selectedFirst())[row]) + } + } + + open override func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { + if component == 0 { + let a = pickerRow.firstOptions()[row] + if let value = pickerRow.value { + guard value.a != a else { + return + } + if pickerRow.secondOptions(a).contains(value.b) { + pickerRow.value = Tuple(a: a, b: value.b) + pickerView.reloadComponent(1) + return + } else { + pickerRow.value = Tuple(a: a, b: pickerRow.secondOptions(a)[0]) + } + } else { + pickerRow.value = Tuple(a: a, b: pickerRow.secondOptions(a)[0]) + } + pickerView.reloadComponent(1) + pickerView.selectRow(0, inComponent: 1, animated: true) + } else { + let a = pickerRow.selectedFirst() + pickerRow.value = Tuple(a: a, b: pickerRow.secondOptions(a)[row]) + } + } + +} + +// MARK: PickerRow +open class _DoublePickerRow : Row> where A: Equatable, B: Equatable { + + /// Options for first component. Will be called often so should be O(1) + public var firstOptions: (() -> [A]) = {[]} + /// Options for second component given the selected value from the first component. Will be called often so should be O(1) + public var secondOptions: ((A) -> [B]) = {_ in []} + + /// Modify the displayed values for the first picker row. + public var displayValueForFirstRow: ((A) -> (String)) = { a in return String(describing: a) } + /// Modify the displayed values for the second picker row. + public var displayValueForSecondRow: ((B) -> (String)) = { b in return String(describing: b) } + + required public init(tag: String?) { + super.init(tag: tag) + } + + func selectedFirst() -> A { + return value?.a ?? firstOptions()[0] + } + +} + +/// A generic row where the user can pick an option from a picker view +public final class DoublePickerRow: _DoublePickerRow, RowType where A: Equatable, B: Equatable { + + required public init(tag: String?) { + super.init(tag: tag) + } + +} diff --git a/Pods/Eureka/Source/Rows/FieldsRow.swift b/Pods/Eureka/Source/Rows/FieldsRow.swift index e1be05c..5a13776 100644 --- a/Pods/Eureka/Source/Rows/FieldsRow.swift +++ b/Pods/Eureka/Source/Rows/FieldsRow.swift @@ -24,16 +24,16 @@ import Foundation -open class TextCell : _FieldCell, CellType { - +open class TextCell: _FieldCell, CellType { + required public init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) } - + required public init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") + super.init(coder: aDecoder) } - + open override func setup() { super.setup() textField.autocorrectionType = .default @@ -42,17 +42,16 @@ open class TextCell : _FieldCell, CellType { } } +open class IntCell: _FieldCell, CellType { -open class IntCell : _FieldCell, CellType { - required public init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) } - + required public init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") + super.init(coder: aDecoder) } - + open override func setup() { super.setup() textField.autocorrectionType = .default @@ -61,32 +60,32 @@ open class IntCell : _FieldCell, CellType { } } -open class PhoneCell : _FieldCell, CellType { - +open class PhoneCell: _FieldCell, CellType { + required public init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) } - + required public init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") + super.init(coder: aDecoder) } - + open override func setup() { super.setup() textField.keyboardType = .phonePad } } -open class NameCell : _FieldCell, CellType { - +open class NameCell: _FieldCell, CellType { + required public init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) } - + required public init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") + super.init(coder: aDecoder) } - + open override func setup() { super.setup() textField.autocorrectionType = .no @@ -95,16 +94,16 @@ open class NameCell : _FieldCell, CellType { } } -open class EmailCell : _FieldCell, CellType { - +open class EmailCell: _FieldCell, CellType { + required public init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) } - + required public init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") + super.init(coder: aDecoder) } - + open override func setup() { super.setup() textField.autocorrectionType = .no @@ -113,16 +112,16 @@ open class EmailCell : _FieldCell, CellType { } } -open class PasswordCell : _FieldCell, CellType { - +open class PasswordCell: _FieldCell, CellType { + required public init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) } - + required public init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") + super.init(coder: aDecoder) } - + open override func setup() { super.setup() textField.autocorrectionType = .no @@ -132,16 +131,16 @@ open class PasswordCell : _FieldCell, CellType { } } -open class DecimalCell : _FieldCell, CellType { - +open class DecimalCell: _FieldCell, CellType { + required public init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) } - + required public init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") + super.init(coder: aDecoder) } - + open override func setup() { super.setup() textField.autocorrectionType = .no @@ -149,16 +148,16 @@ open class DecimalCell : _FieldCell, CellType { } } -open class URLCell : _FieldCell, CellType { - +open class URLCell: _FieldCell, CellType { + required public init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) } - + required public init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") + super.init(coder: aDecoder) } - + open override func setup() { super.setup() textField.autocorrectionType = .no @@ -167,16 +166,16 @@ open class URLCell : _FieldCell, CellType { } } -open class TwitterCell : _FieldCell, CellType { - +open class TwitterCell: _FieldCell, CellType { + required public init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) } - + required public init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") + super.init(coder: aDecoder) } - + open override func setup() { super.setup() textField.autocorrectionType = .no @@ -185,16 +184,16 @@ open class TwitterCell : _FieldCell, CellType { } } -open class AccountCell : _FieldCell, CellType { - +open class AccountCell: _FieldCell, CellType { + required public init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) } - + required public init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") + super.init(coder: aDecoder) } - + open override func setup() { super.setup() textField.autocorrectionType = .no @@ -203,16 +202,16 @@ open class AccountCell : _FieldCell, CellType { } } -open class ZipCodeCell : _FieldCell, CellType { - +open class ZipCodeCell: _FieldCell, CellType { + required public init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) } - + required public init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") + super.init(coder: aDecoder) } - + open override func update() { super.update() textField.autocorrectionType = .no @@ -262,7 +261,6 @@ open class _PasswordRow: FieldRow { } } - open class _DecimalRow: FieldRow { public required init(tag: String?) { super.init(tag: tag) @@ -298,7 +296,6 @@ open class _ZipCodeRow: FieldRow { } } - /// A String valued row where the user can enter arbitrary text. public final class TextRow: _TextRow, RowType { required public init(tag: String?) { diff --git a/Pods/Eureka/Source/Rows/LabelRow.swift b/Pods/Eureka/Source/Rows/LabelRow.swift index 853c795..70d1632 100644 --- a/Pods/Eureka/Source/Rows/LabelRow.swift +++ b/Pods/Eureka/Source/Rows/LabelRow.swift @@ -27,15 +27,15 @@ import Foundation // MARK: LabelCell open class LabelCellOf: Cell, CellType { - + required public init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) } - + required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } - + open override func setup() { super.setup() selectionStyle = .none diff --git a/Pods/Eureka/Source/Rows/MultipleSelectorRow.swift b/Pods/Eureka/Source/Rows/MultipleSelectorRow.swift index 30c2b21..466a285 100644 --- a/Pods/Eureka/Source/Rows/MultipleSelectorRow.swift +++ b/Pods/Eureka/Source/Rows/MultipleSelectorRow.swift @@ -24,7 +24,7 @@ import Foundation -open class _MultipleSelectorRow: GenericMultipleSelectorRow> where Cell: BaseCell, Cell: TypedCellType, Cell.Value == Set { +open class _MultipleSelectorRow: GenericMultipleSelectorRow where Cell: BaseCell, Cell.Value == Set { public required init(tag: String?) { super.init(tag: tag) } diff --git a/Pods/Eureka/Source/Rows/PickerInlineRow.swift b/Pods/Eureka/Source/Rows/PickerInlineRow.swift index 209e1d9..9e15be9 100644 --- a/Pods/Eureka/Source/Rows/PickerInlineRow.swift +++ b/Pods/Eureka/Source/Rows/PickerInlineRow.swift @@ -25,40 +25,40 @@ import Foundation open class PickerInlineCell : Cell, CellType { - + required public init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) } - + required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } - + open override func setup() { super.setup() accessoryType = .none editingAccessoryType = .none } - + open override func update() { super.update() selectionStyle = row.isDisabled ? .none : .default } - + open override func didSelect() { super.didSelect() row.deselect() } } -//MARK: PickerInlineRow +// MARK: PickerInlineRow open class _PickerInlineRow : Row>, NoValueDisplayTextConformance where T: Equatable { - + public typealias InlineRow = PickerRow open var options = [T]() open var noValueDisplayText: String? - + required public init(tag: String?) { super.init(tag: tag) } @@ -66,7 +66,7 @@ open class _PickerInlineRow : Row>, NoValueDisplayTextCon /// A generic inline row where the user can pick an option from a picker view which shows and hides itself automatically public final class PickerInlineRow : _PickerInlineRow, RowType, InlineRowType where T: Equatable { - + required public init(tag: String?) { super.init(tag: tag) onExpandInlineRow { cell, row, _ in @@ -77,17 +77,125 @@ public final class PickerInlineRow : _PickerInlineRow, RowType, InlineRowT cell.detailTextLabel?.textColor = cell.tintColor } } - + public override func customDidSelect() { super.customDidSelect() if !isDisabled { toggleInlineRow() } } - + public func setupInlineRow(_ inlineRow: InlineRow) { inlineRow.options = self.options inlineRow.displayValueFor = self.displayValueFor + inlineRow.cell.height = { UITableViewAutomaticDimension } } } +open class _DoublePickerInlineRow : Row>>, NoValueDisplayTextConformance where A: Equatable, B: Equatable { + + public typealias InlineRow = DoublePickerRow + + /// Options for first component. Will be called often so should be O(1) + public var firstOptions: (() -> [A]) = {[]} + + /// Options for second component given the selected value from the first component. Will be called often so should be O(1) + public var secondOptions: ((A) -> [B]) = { _ in [] } + + public var noValueDisplayText: String? + + required public init(tag: String?) { + super.init(tag: tag) + self.displayValueFor = { [weak self] tuple in + if let tuple = tuple { + return String(describing: tuple.a) + ", " + String(describing: tuple.b) + } + return self?.noValueDisplayText + } + } +} + +/// A generic inline row where the user can pick an option from a picker view which shows and hides itself automatically +public final class DoublePickerInlineRow : _DoublePickerInlineRow, RowType, InlineRowType where A: Equatable, B: Equatable { + + required public init(tag: String?) { + super.init(tag: tag) + onExpandInlineRow { cell, row, _ in + let color = cell.detailTextLabel?.textColor + row.onCollapseInlineRow { cell, _, _ in + cell.detailTextLabel?.textColor = color + } + cell.detailTextLabel?.textColor = cell.tintColor + } + } + + public override func customDidSelect() { + super.customDidSelect() + if !isDisabled { + toggleInlineRow() + } + } + + public func setupInlineRow(_ inlineRow: InlineRow) { + inlineRow.firstOptions = firstOptions + inlineRow.secondOptions = secondOptions + inlineRow.displayValueFor = self.displayValueFor + inlineRow.cell.height = { UITableViewAutomaticDimension } + } +} + +open class _TriplePickerInlineRow : Row>>, NoValueDisplayTextConformance + where A: Equatable, B: Equatable, C: Equatable { + + public typealias InlineRow = TriplePickerRow + + /// Options for first component. Will be called often so should be O(1) + public var firstOptions: (() -> [A]) = {[]} + /// Options for second component given the selected value from the first component. Will be called often so should be O(1) + public var secondOptions: ((A) -> [B]) = { _ in [] } + /// Options for third component given the selected value from the first and second components. Will be called often so should be O(1) + public var thirdOptions: ((A, B) -> [C]) = {_, _ in []} + + open var noValueDisplayText: String? + + required public init(tag: String?) { + super.init(tag: tag) + self.displayValueFor = { [weak self] tuple in + if let tuple = tuple { + return String(describing: tuple.a) + ", " + String(describing: tuple.b) + ", " + String(describing: tuple.c) + } + return self?.noValueDisplayText + } + } +} + +/// A generic inline row where the user can pick an option from a picker view which shows and hides itself automatically +public final class TriplePickerInlineRow : _TriplePickerInlineRow, RowType, InlineRowType + where A: Equatable, B: Equatable, C: Equatable { + + required public init(tag: String?) { + super.init(tag: tag) + onExpandInlineRow { cell, row, _ in + let color = cell.detailTextLabel?.textColor + row.onCollapseInlineRow { cell, _, _ in + cell.detailTextLabel?.textColor = color + } + cell.detailTextLabel?.textColor = cell.tintColor + } + } + + public override func customDidSelect() { + super.customDidSelect() + if !isDisabled { + toggleInlineRow() + } + } + + public func setupInlineRow(_ inlineRow: InlineRow) { + inlineRow.firstOptions = firstOptions + inlineRow.secondOptions = secondOptions + inlineRow.thirdOptions = thirdOptions + inlineRow.displayValueFor = self.displayValueFor + inlineRow.cell.height = { UITableViewAutomaticDimension } + } +} diff --git a/Pods/Eureka/Source/Rows/PickerInputRow.swift b/Pods/Eureka/Source/Rows/PickerInputRow.swift index ac4d08d..d54ec4a 100644 --- a/Pods/Eureka/Source/Rows/PickerInputRow.swift +++ b/Pods/Eureka/Source/Rows/PickerInputRow.swift @@ -24,25 +24,26 @@ import Foundation -//MARK: PickerInputCell +// MARK: PickerInputCell -open class PickerInputCell : Cell, CellType, UIPickerViewDataSource, UIPickerViewDelegate where T: Equatable, T: InputTypeInitiable { - - public var picker: UIPickerView - - private var pickerInputRow : _PickerInputRow? { return row as? _PickerInputRow } - - public required init(style: UITableViewCellStyle, reuseIdentifier: String?){ - self.picker = UIPickerView() - self.picker.translatesAutoresizingMaskIntoConstraints = false - +open class _PickerInputCell : Cell, CellType, UIPickerViewDataSource, UIPickerViewDelegate where T: Equatable { + + lazy public var picker: UIPickerView = { + let picker = UIPickerView() + picker.translatesAutoresizingMaskIntoConstraints = false + return picker + }() + + fileprivate var pickerInputRow: _PickerInputRow? { return row as? _PickerInputRow } + + public required init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) } - + required public init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") + super.init(coder: aDecoder) } - + open override func setup() { super.setup() accessoryType = .none @@ -50,90 +51,103 @@ open class PickerInputCell : Cell, CellType, UIPickerViewDataSo picker.delegate = self picker.dataSource = self } - + deinit { picker.delegate = nil picker.dataSource = nil } - - open override func update(){ + + open override func update() { super.update() selectionStyle = row.isDisabled ? .none : .default - + if row.title?.isEmpty == false { detailTextLabel?.text = row.displayValueFor?(row.value) ?? (row as? NoValueDisplayTextConformance)?.noValueDisplayText - } - else { + } else { textLabel?.text = row.displayValueFor?(row.value) ?? (row as? NoValueDisplayTextConformance)?.noValueDisplayText detailTextLabel?.text = nil } - + textLabel?.textColor = row.isDisabled ? .gray : .black if row.isHighlighted { textLabel?.textColor = tintColor } - + picker.reloadAllComponents() - if let selectedValue = pickerInputRow?.value, let index = pickerInputRow?.options.index(of: selectedValue){ - picker.selectRow(index, inComponent: 0, animated: true) - } - } - + open override func didSelect() { super.didSelect() row.deselect() } - + open override var inputView: UIView? { return picker } - + open override func cellCanBecomeFirstResponder() -> Bool { return canBecomeFirstResponder } - + override open var canBecomeFirstResponder: Bool { return !row.isDisabled } - + open func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 } - + open func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return pickerInputRow?.options.count ?? 0 } - + open func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { return pickerInputRow?.displayValueFor?(pickerInputRow?.options[row]) } - + open func pickerView(_ pickerView: UIPickerView, didSelectRow rowNumber: Int, inComponent component: Int) { if let picker = pickerInputRow, picker.options.count > rowNumber { picker.value = picker.options[rowNumber] update() } } +} + +open class PickerInputCell: _PickerInputCell where T: Equatable { + + public required init(style: UITableViewCellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + open override func update() { + super.update() + if let selectedValue = pickerInputRow?.value, let index = pickerInputRow?.options.index(of: selectedValue) { + picker.selectRow(index, inComponent: 0, animated: true) + } + } + } -//MARK: PickerInputRow +// MARK: PickerInputRow -open class _PickerInputRow : Row>, NoValueDisplayTextConformance where T: Equatable, T: InputTypeInitiable { +open class _PickerInputRow : Row>, NoValueDisplayTextConformance where T: Equatable { open var noValueDisplayText: String? = nil - + open var options = [T]() - + required public init(tag: String?) { super.init(tag: tag) - + } } /// A generic row where the user can pick an option from a picker view displayed in the keyboard area public final class PickerInputRow: _PickerInputRow, RowType where T: Equatable, T: InputTypeInitiable { - + required public init(tag: String?) { super.init(tag: tag) } diff --git a/Pods/Eureka/Source/Rows/PickerRow.swift b/Pods/Eureka/Source/Rows/PickerRow.swift index 37b74ce..d254b8b 100644 --- a/Pods/Eureka/Source/Rows/PickerRow.swift +++ b/Pods/Eureka/Source/Rows/PickerRow.swift @@ -24,78 +24,96 @@ import Foundation -//MARK: PickerCell - -open class PickerCell : Cell, CellType, UIPickerViewDataSource, UIPickerViewDelegate where T: Equatable{ - - public var picker: UIPickerView - - private var pickerRow : _PickerRow? { return row as? _PickerRow } - - public required init(style: UITableViewCellStyle, reuseIdentifier: String?){ - self.picker = UIPickerView() - self.picker.translatesAutoresizingMaskIntoConstraints = false - +// MARK: PickerCell + +open class _PickerCell : Cell, CellType, UIPickerViewDataSource, UIPickerViewDelegate where T: Equatable { + + @IBOutlet public weak var picker: UIPickerView! + + fileprivate var pickerRow: _PickerRow? { return row as? _PickerRow } + + public required init(style: UITableViewCellStyle, reuseIdentifier: String?) { + let pickerView = UIPickerView() + self.picker = pickerView + self.picker?.translatesAutoresizingMaskIntoConstraints = false + super.init(style: style, reuseIdentifier: reuseIdentifier) - - self.contentView.addSubview(self.picker) - self.contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[picker]-0-|", options: [], metrics: nil, views: ["picker": self.picker])) - self.contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[picker]-0-|", options: [], metrics: nil, views: ["picker": self.picker])) + + self.contentView.addSubview(pickerView) + self.contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[picker]-0-|", options: [], metrics: nil, views: ["picker": pickerView])) + self.contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[picker]-0-|", options: [], metrics: nil, views: ["picker": pickerView])) } - + required public init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") + super.init(coder: aDecoder) } - + open override func setup() { super.setup() accessoryType = .none editingAccessoryType = .none + height = { UITableViewAutomaticDimension } picker.delegate = self picker.dataSource = self } - - deinit { - picker.delegate = nil - picker.dataSource = nil - } - - open override func update(){ + + open override func update() { super.update() textLabel?.text = nil detailTextLabel?.text = nil picker.reloadAllComponents() - if let selectedValue = pickerRow?.value, let index = pickerRow?.options.index(of: selectedValue){ - picker.selectRow(index, inComponent: 0, animated: true) - } } - + + deinit { + picker?.delegate = nil + picker?.dataSource = nil + } + open func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 } - + open func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return pickerRow?.options.count ?? 0 } - + open func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { return pickerRow?.displayValueFor?(pickerRow?.options[row]) } - + open func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { if let picker = pickerRow, !picker.options.isEmpty { picker.value = picker.options[row] } } - + } -//MARK: PickerRow +open class PickerCell : _PickerCell where T: Equatable { + + required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + public required init(style: UITableViewCellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + open override func update() { + super.update() + if let selectedValue = pickerRow?.value, let index = pickerRow?.options.index(of: selectedValue) { + picker.selectRow(index, inComponent: 0, animated: true) + } + } + +} + +// MARK: PickerRow + +open class _PickerRow : Row> where T: Equatable { -open class _PickerRow : Row> where T: Equatable{ - open var options = [T]() - + required public init(tag: String?) { super.init(tag: tag) } @@ -103,7 +121,7 @@ open class _PickerRow : Row> where T: Equatable{ /// A generic row where the user can pick an option from a picker view public final class PickerRow: _PickerRow, RowType where T: Equatable { - + required public init(tag: String?) { super.init(tag: tag) } diff --git a/Pods/Eureka/Source/Rows/PopoverSelectorRow.swift b/Pods/Eureka/Source/Rows/PopoverSelectorRow.swift index c73c6ba..1cdf861 100644 --- a/Pods/Eureka/Source/Rows/PopoverSelectorRow.swift +++ b/Pods/Eureka/Source/Rows/PopoverSelectorRow.swift @@ -24,8 +24,8 @@ import Foundation -open class _PopoverSelectorRow : SelectorRow> where Cell: BaseCell, Cell: TypedCellType { - +open class _PopoverSelectorRow : SelectorRow where Cell: BaseCell { + public required init(tag: String?) { super.init(tag: tag) onPresentCallback = { [weak self] (_, viewController) -> Void in @@ -35,12 +35,12 @@ open class _PopoverSelectorRow : SelectorRow(){ _ in } }, onDismiss: { [weak self] in + presentationMode = .popover(controllerProvider: ControllerProvider.callback { return SelectorViewController> { _ in } }, onDismiss: { [weak self] in $0.dismiss(animated: true) self?.reload() - }) + }) } - + open override func didSelect() { deselect() super.didSelect() diff --git a/Pods/Eureka/Source/Rows/PushRow.swift b/Pods/Eureka/Source/Rows/PushRow.swift index 6571716..ef92965 100644 --- a/Pods/Eureka/Source/Rows/PushRow.swift +++ b/Pods/Eureka/Source/Rows/PushRow.swift @@ -24,11 +24,11 @@ import Foundation -open class _PushRow : SelectorRow> where Cell: BaseCell { - +open class _PushRow: SelectorRow where Cell: BaseCell { + public required init(tag: String?) { super.init(tag: tag) - presentationMode = .show(controllerProvider: ControllerProvider.callback { return SelectorViewController(){ _ in } }, onDismiss: { vc in + presentationMode = .show(controllerProvider: ControllerProvider.callback { return SelectorViewController> { _ in } }, onDismiss: { vc in let _ = vc.navigationController?.popViewController(animated: true) }) } } diff --git a/Pods/Eureka/Source/Rows/SegmentedRow.swift b/Pods/Eureka/Source/Rows/SegmentedRow.swift index 29b9477..acc4e6c 100644 --- a/Pods/Eureka/Source/Rows/SegmentedRow.swift +++ b/Pods/Eureka/Source/Rows/SegmentedRow.swift @@ -24,152 +24,165 @@ import Foundation -//MARK: SegmentedCell +// MARK: SegmentedCell open class SegmentedCell : Cell, CellType { - - open var titleLabel : UILabel? { - textLabel?.translatesAutoresizingMaskIntoConstraints = false - textLabel?.setContentHuggingPriority(500, for: .horizontal) - return textLabel - } - lazy open var segmentedControl : UISegmentedControl = { - let result = UISegmentedControl() - result.translatesAutoresizingMaskIntoConstraints = false - result.setContentHuggingPriority(250, for: .horizontal) - return result - }() + + @IBOutlet public weak var segmentedControl: UISegmentedControl! + @IBOutlet public weak var titleLabel: UILabel? + private var dynamicConstraints = [NSLayoutConstraint]() - fileprivate var observingTitleText: Bool = false - + fileprivate var observingTitleText = false + private var awakeFromNibCalled = false + required public init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) - NotificationCenter.default.addObserver(forName: Notification.Name.UIApplicationWillResignActive, object: nil, queue: nil){ [weak self] notification in + + let segmentedControl = UISegmentedControl() + segmentedControl.translatesAutoresizingMaskIntoConstraints = false + segmentedControl.setContentHuggingPriority(UILayoutPriority(rawValue: 250), for: .horizontal) + self.segmentedControl = segmentedControl + + self.titleLabel = self.textLabel + self.titleLabel?.translatesAutoresizingMaskIntoConstraints = false + self.titleLabel?.setContentHuggingPriority(UILayoutPriority(rawValue: 500), for: .horizontal) + + NotificationCenter.default.addObserver(forName: Notification.Name.UIApplicationWillResignActive, object: nil, queue: nil) { [weak self] _ in guard let me = self else { return } guard me.observingTitleText else { return } me.titleLabel?.removeObserver(me, forKeyPath: "text") me.observingTitleText = false } - NotificationCenter.default.addObserver(forName: Notification.Name.UIApplicationDidBecomeActive, object: nil, queue: nil){ [weak self] notification in + NotificationCenter.default.addObserver(forName: Notification.Name.UIApplicationDidBecomeActive, object: nil, queue: nil) { [weak self] _ in guard let me = self else { return } guard !me.observingTitleText else { return } me.titleLabel?.addObserver(me, forKeyPath: "text", options: NSKeyValueObservingOptions.old.union(.new), context: nil) me.observingTitleText = true } - - NotificationCenter.default.addObserver(forName: Notification.Name.UIContentSizeCategoryDidChange, object: nil, queue: nil){ [weak self] notification in + + NotificationCenter.default.addObserver(forName: Notification.Name.UIContentSizeCategoryDidChange, object: nil, queue: nil) { [weak self] _ in + self?.titleLabel = self?.textLabel self?.setNeedsUpdateConstraints() } + contentView.addSubview(titleLabel!) + contentView.addSubview(segmentedControl) + titleLabel?.addObserver(self, forKeyPath: "text", options: [.old, .new], context: nil) + observingTitleText = true + imageView?.addObserver(self, forKeyPath: "image", options: [.old, .new], context: nil) + + contentView.addConstraint(NSLayoutConstraint(item: segmentedControl, attribute: .centerY, relatedBy: .equal, toItem: contentView, attribute: .centerY, multiplier: 1, constant: 0)) + } - + required public init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") + super.init(coder: aDecoder) + } + + open override func awakeFromNib() { + super.awakeFromNib() + awakeFromNibCalled = true } - + deinit { segmentedControl.removeTarget(self, action: nil, for: .allEvents) - if observingTitleText { - titleLabel?.removeObserver(self, forKeyPath: "text") + if !awakeFromNibCalled { + if observingTitleText { + titleLabel?.removeObserver(self, forKeyPath: "text") + } + imageView?.removeObserver(self, forKeyPath: "image") + NotificationCenter.default.removeObserver(self, name: Notification.Name.UIApplicationWillResignActive, object: nil) + NotificationCenter.default.removeObserver(self, name: Notification.Name.UIApplicationDidBecomeActive, object: nil) + NotificationCenter.default.removeObserver(self, name: Notification.Name.UIContentSizeCategoryDidChange, object: nil) } - imageView?.removeObserver(self, forKeyPath: "image") - NotificationCenter.default.removeObserver(self, name: Notification.Name.UIApplicationWillResignActive, object: nil) - NotificationCenter.default.removeObserver(self, name: Notification.Name.UIApplicationDidBecomeActive, object: nil) - NotificationCenter.default.removeObserver(self, name: Notification.Name.UIContentSizeCategoryDidChange, object: nil) + } - + open override func setup() { super.setup() - height = { BaseRow.estimatedRowHeight } selectionStyle = .none - contentView.addSubview(titleLabel!) - contentView.addSubview(segmentedControl) - titleLabel?.addObserver(self, forKeyPath: "text", options: [.old, .new], context: nil) - observingTitleText = true - imageView?.addObserver(self, forKeyPath: "image", options: [.old, .new], context: nil) segmentedControl.addTarget(self, action: #selector(SegmentedCell.valueChanged), for: .valueChanged) - contentView.addConstraint(NSLayoutConstraint(item: segmentedControl, attribute: .centerY, relatedBy: .equal, toItem: contentView, attribute: .centerY, multiplier: 1, constant: 0)) } - + open override func update() { super.update() detailTextLabel?.text = nil - + updateSegmentedControl() segmentedControl.selectedSegmentIndex = selectedIndex() ?? UISegmentedControlNoSegment segmentedControl.isEnabled = !row.isDisabled } - - func valueChanged() { - row.value = (row as! SegmentedRow).options[segmentedControl.selectedSegmentIndex] + + @objc func valueChanged() { + row.value = (row as! SegmentedRow).options?[segmentedControl.selectedSegmentIndex] } - + open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { let obj = object as AnyObject? - - if let changeType = change, let _ = keyPath, ((obj === titleLabel && keyPath == "text") || (obj === imageView && keyPath == "image")) && (changeType[NSKeyValueChangeKey.kindKey] as? NSNumber)?.uintValue == NSKeyValueChange.setting.rawValue{ + + if let changeType = change, let _ = keyPath, ((obj === titleLabel && keyPath == "text") || (obj === imageView && keyPath == "image")) && + (changeType[NSKeyValueChangeKey.kindKey] as? NSNumber)?.uintValue == NSKeyValueChange.setting.rawValue, !awakeFromNibCalled { setNeedsUpdateConstraints() updateConstraintsIfNeeded() } } - + func updateSegmentedControl() { segmentedControl.removeAllSegments() - items().enumerated().forEach { segmentedControl.insertSegment(withTitle: $0.element, at: $0.offset, animated: false) } + + (row as! SegmentedRow).options?.reversed().forEach { + if let image = $0 as? UIImage { + segmentedControl.insertSegment(with: image, at: 0, animated: false) + } else { + segmentedControl.insertSegment(withTitle: row.displayValueFor?($0) ?? "", at: 0, animated: false) + } + } } - + open override func updateConstraints() { + guard !awakeFromNibCalled else { + super.updateConstraints() + return + } contentView.removeConstraints(dynamicConstraints) dynamicConstraints = [] - var views : [String: AnyObject] = ["segmentedControl": segmentedControl] - + var views: [String: AnyObject] = ["segmentedControl": segmentedControl] + var hasImageView = false var hasTitleLabel = false - + if let imageView = imageView, let _ = imageView.image { views["imageView"] = imageView hasImageView = true } - + if let titleLabel = titleLabel, let text = titleLabel.text, !text.isEmpty { views["titleLabel"] = titleLabel hasTitleLabel = true dynamicConstraints.append(NSLayoutConstraint(item: titleLabel, attribute: .centerY, relatedBy: .equal, toItem: contentView, attribute: .centerY, multiplier: 1, constant: 0)) } - + dynamicConstraints.append(NSLayoutConstraint(item: segmentedControl, attribute: .width, relatedBy: .greaterThanOrEqual, toItem: contentView, attribute: .width, multiplier: 0.3, constant: 0.0)) - - + if hasImageView && hasTitleLabel { dynamicConstraints += NSLayoutConstraint.constraints(withVisualFormat: "H:[imageView]-(15)-[titleLabel]-[segmentedControl]-|", options: [], metrics: nil, views: views) - } - else if hasImageView && !hasTitleLabel { + } else if hasImageView && !hasTitleLabel { dynamicConstraints += NSLayoutConstraint.constraints(withVisualFormat: "H:[imageView]-[segmentedControl]-|", options: [], metrics: nil, views: views) - } - else if !hasImageView && hasTitleLabel { + } else if !hasImageView && hasTitleLabel { dynamicConstraints = NSLayoutConstraint.constraints(withVisualFormat: "H:|-[titleLabel]-[segmentedControl]-|", options: .alignAllCenterY, metrics: nil, views: views) - } - else { + } else { dynamicConstraints = NSLayoutConstraint.constraints(withVisualFormat: "H:|-[segmentedControl]-|", options: .alignAllCenterY, metrics: nil, views: views) } contentView.addConstraints(dynamicConstraints) super.updateConstraints() } - - func items() -> [String] {// or create protocol for options - var result = [String]() - for object in (row as! SegmentedRow).options { - result.append(row.displayValueFor?(object) ?? "") - } - return result - } - + func selectedIndex() -> Int? { guard let value = row.value else { return nil } - return (row as! SegmentedRow).options.index(of: value) + return (row as! SegmentedRow).options?.index(of: value) } } -//MARK: SegmentedRow +// MARK: SegmentedRow /// An options row where the user can select an option from an UISegmentedControl public final class SegmentedRow: OptionsRow>, RowType { @@ -177,4 +190,3 @@ public final class SegmentedRow: OptionsRow>, Row super.init(tag: tag) } } - diff --git a/Pods/Eureka/Source/Rows/SelectableRows/ListCheckRow.swift b/Pods/Eureka/Source/Rows/SelectableRows/ListCheckRow.swift index 48347a1..7eaa7d1 100644 --- a/Pods/Eureka/Source/Rows/SelectableRows/ListCheckRow.swift +++ b/Pods/Eureka/Source/Rows/SelectableRows/ListCheckRow.swift @@ -25,46 +25,42 @@ import Foundation open class ListCheckCell : Cell, CellType { - + required public init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) } - + required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } - + open override func update() { super.update() accessoryType = row.value != nil ? .checkmark : .none editingAccessoryType = accessoryType selectionStyle = .default - var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0 - tintColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha) if row.isDisabled { - tintColor = UIColor(red: red, green: green, blue: blue, alpha: 0.3) + tintAdjustmentMode = .dimmed selectionStyle = .none - } - else { - tintColor = UIColor(red: red, green: green, blue: blue, alpha: 1) + } else { + tintAdjustmentMode = .automatic } } - + open override func setup() { super.setup() accessoryType = .checkmark editingAccessoryType = accessoryType } - + open override func didSelect() { row.deselect() row.updateCell() } - -} +} -public final class ListCheckRow: Row>, SelectableRowType, RowType { +public final class ListCheckRow: Row>, SelectableRowType, RowType where T: Equatable { public var selectableValue: T? required public init(tag: String?) { super.init(tag: tag) diff --git a/Pods/Eureka/Source/Rows/SliderRow.swift b/Pods/Eureka/Source/Rows/SliderRow.swift index 43dc160..261ea87 100644 --- a/Pods/Eureka/Source/Rows/SliderRow.swift +++ b/Pods/Eureka/Source/Rows/SliderRow.swift @@ -26,119 +26,150 @@ import UIKit /// The cell of the SliderRow open class SliderCell: Cell, CellType { - + + private var awakeFromNibCalled = false + + @IBOutlet open weak var titleLabel: UILabel! + @IBOutlet open weak var valueLabel: UILabel! + @IBOutlet open weak var slider: UISlider! + + open var formatter: NumberFormatter? + public required init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: .value1, reuseIdentifier: reuseIdentifier) - NotificationCenter.default.addObserver(forName: Notification.Name.UIContentSizeCategoryDidChange, object: nil, queue: nil){ [weak self] notification in + + NotificationCenter.default.addObserver(forName: Notification.Name.UIContentSizeCategoryDidChange, object: nil, queue: nil) { [weak self] _ in guard let me = self else { return } - if me.shouldShowTitle() { - me.contentView.addSubview(me.titleLabel) - me.contentView.addSubview(me.valueLabel!) - me.addConstraints() + if me.shouldShowTitle { + me.titleLabel = me.textLabel + me.valueLabel = me.detailTextLabel + me.setNeedsUpdateConstraints() } } } - + deinit { + guard !awakeFromNibCalled else { return } NotificationCenter.default.removeObserver(self, name: Notification.Name.UIContentSizeCategoryDidChange, object: nil) } - + required public init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") + super.init(coder: aDecoder) + awakeFromNibCalled = true } - - open var titleLabel: UILabel! { - textLabel?.translatesAutoresizingMaskIntoConstraints = false - textLabel?.setContentHuggingPriority(500, for: .horizontal) - return textLabel - } - open var valueLabel: UILabel! { - detailTextLabel?.translatesAutoresizingMaskIntoConstraints = false - detailTextLabel?.setContentHuggingPriority(500, for: .horizontal) - return detailTextLabel - } - lazy open var slider: UISlider = { - let result = UISlider() - result.translatesAutoresizingMaskIntoConstraints = false - result.setContentHuggingPriority(500, for: .horizontal) - return result - }() - open var formatter: NumberFormatter? - + open override func setup() { super.setup() + if !awakeFromNibCalled { + let title = textLabel + textLabel?.translatesAutoresizingMaskIntoConstraints = false + textLabel?.setContentHuggingPriority(UILayoutPriority(rawValue: 500), for: .horizontal) + self.titleLabel = title + + let value = detailTextLabel + value?.translatesAutoresizingMaskIntoConstraints = false + value?.setContentHuggingPriority(UILayoutPriority(500), for: .horizontal) + value?.adjustsFontSizeToFitWidth = true + value?.minimumScaleFactor = 0.5 + self.valueLabel = value + + let slider = UISlider() + slider.translatesAutoresizingMaskIntoConstraints = false + slider.setContentHuggingPriority(UILayoutPriority(rawValue: 500), for: .horizontal) + self.slider = slider + + if shouldShowTitle { + contentView.addSubview(titleLabel) + } + + if !sliderRow.shouldHideValue { + contentView.addSubview(valueLabel) + } + contentView.addSubview(slider) + setNeedsUpdateConstraints() + } selectionStyle = .none - slider.minimumValue = sliderRow.minimumValue - slider.maximumValue = sliderRow.maximumValue + slider.minimumValue = 0 + slider.maximumValue = 10 slider.addTarget(self, action: #selector(SliderCell.valueChanged), for: .valueChanged) - - if shouldShowTitle() { - contentView.addSubview(titleLabel) - contentView.addSubview(valueLabel!) - } - contentView.addSubview(slider) - addConstraints() } - + open override func update() { super.update() - if !shouldShowTitle() { - textLabel?.text = nil - valueLabel.text = nil - } else if valueLabel.text == nil { - valueLabel.text = " " - } - slider.value = row.value ?? 0.0 + titleLabel.text = row.title + titleLabel.isHidden = !shouldShowTitle + valueLabel.text = row.displayValueFor?(row.value) + valueLabel.isHidden = sliderRow.shouldHideValue + slider.value = row.value ?? slider.minimumValue + slider.isEnabled = !row.isDisabled + } - - func addConstraints() { - let views: [String : Any] = ["titleLabel" : titleLabel, "valueLabel" : valueLabel, "slider" : slider] - //TODO: in Iphone 6 Plus hPadding should be 20 - let metrics = ["hPadding" : 15.0, "vPadding" : 12.0, "spacing" : 12.0] - if shouldShowTitle() { - contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-hPadding-[titleLabel]-[valueLabel]-hPadding-|", options: NSLayoutFormatOptions.alignAllLastBaseline, metrics: metrics, views: views)) - contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-vPadding-[titleLabel]-spacing-[slider]-vPadding-|", options: NSLayoutFormatOptions.alignAllLeft, metrics: metrics, views: views)) - - } else { - contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-vPadding-[slider]-vPadding-|", options: NSLayoutFormatOptions.alignAllLeft, metrics: metrics, views: views)) - } - contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-hPadding-[slider]-hPadding-|", options: NSLayoutFormatOptions.alignAllLastBaseline, metrics: metrics, views: views)) - } - - func valueChanged() { + @objc func valueChanged() { let roundedValue: Float let steps = Float(sliderRow.steps) if steps > 0 { let stepValue = round((slider.value - slider.minimumValue) / (slider.maximumValue - slider.minimumValue) * steps) let stepAmount = (slider.maximumValue - slider.minimumValue) / steps roundedValue = stepValue * stepAmount + self.slider.minimumValue - } - else { + } else { roundedValue = slider.value } row.value = roundedValue row.updateCell() } - - private func shouldShowTitle() -> Bool { - return row.title?.isEmpty == false + + var shouldShowTitle: Bool { + return row?.title?.isEmpty == false } - + private var sliderRow: SliderRow { return row as! SliderRow } + + open override func updateConstraints() { + customConstraints() + super.updateConstraints() + } + + open var dynamicConstraints = [NSLayoutConstraint]() + + open func customConstraints() { + guard !awakeFromNibCalled else { return } + contentView.removeConstraints(dynamicConstraints) + dynamicConstraints = [] + + var views: [String : Any] = ["titleLabel": titleLabel, "slider": slider, "valueLabel": valueLabel] + let metrics = ["spacing": 15.0] + valueLabel.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) + titleLabel.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) + + let title = shouldShowTitle ? "[titleLabel]-spacing-" : "" + let value = !sliderRow.shouldHideValue ? "-[valueLabel]" : "" + + if let imageView = imageView, let _ = imageView.image { + views["imageView"] = imageView + let hContraints = NSLayoutConstraint.constraints(withVisualFormat: "H:[imageView]-(15)-\(title)[slider]\(value)-|", options: .alignAllCenterY, metrics: metrics, views: views) + dynamicConstraints.append(contentsOf: hContraints) + } else { + let hContraints = NSLayoutConstraint.constraints(withVisualFormat: "H:|-\(title)[slider]\(value)-|", options: .alignAllCenterY, metrics: metrics, views: views) + dynamicConstraints.append(contentsOf: hContraints) + } + let vContraint = NSLayoutConstraint(item: slider, attribute: .centerY, relatedBy: .equal, toItem: contentView, attribute: .centerY, multiplier: 1, constant: 0) + dynamicConstraints.append(vContraint) + contentView.addConstraints(dynamicConstraints) + } + } + /// A row that displays a UISlider. If there is a title set then the title and value will appear above the UISlider. public final class SliderRow: Row, RowType { - - public var minimumValue: Float = 0.0 - public var maximumValue: Float = 10.0 + public var steps: UInt = 20 - + public var shouldHideValue = false + required public init(tag: String?) { super.init(tag: tag) } } - diff --git a/Pods/Eureka/Source/Rows/StepperRow.swift b/Pods/Eureka/Source/Rows/StepperRow.swift index a00af8b..689aedf 100644 --- a/Pods/Eureka/Source/Rows/StepperRow.swift +++ b/Pods/Eureka/Source/Rows/StepperRow.swift @@ -10,61 +10,58 @@ import UIKit // MARK: StepperCell -open class StepperCell : Cell, CellType { - - public typealias Value = Double - +open class StepperCell: Cell, CellType { + + @IBOutlet public weak var stepper: UIStepper! + @IBOutlet public weak var valueLabel: UILabel? + required public init(style: UITableViewCellStyle, reuseIdentifier: String?) { - self.stepper = UIStepper() + let stepper = UIStepper() + self.stepper = stepper self.stepper.translatesAutoresizingMaskIntoConstraints = false - self.valueLabel = UILabel() - self.valueLabel.translatesAutoresizingMaskIntoConstraints = false - self.valueLabel.numberOfLines = 1 + let valueLabel = UILabel() + self.valueLabel = valueLabel + self.valueLabel?.translatesAutoresizingMaskIntoConstraints = false + self.valueLabel?.numberOfLines = 1 super.init(style: style, reuseIdentifier: reuseIdentifier) - height = { BaseRow.estimatedRowHeight } + + addSubview(stepper) + addSubview(valueLabel) + + addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[v]-[s]-|", options: .alignAllCenterY, metrics: nil, views: ["s": stepper, "v": valueLabel])) + addConstraint(NSLayoutConstraint(item: stepper, attribute: .centerY, relatedBy: .equal, toItem: contentView, attribute: .centerY, multiplier: 1.0, constant: 0)) + addConstraint(NSLayoutConstraint(item: valueLabel, attribute: .centerY, relatedBy: .equal, toItem: stepper, attribute: .centerY, multiplier: 1.0, constant: 0)) } - + required public init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") + super.init(coder: aDecoder) } - - public var stepper: UIStepper - - public var valueLabel: UILabel - + open override func setup() { super.setup() selectionStyle = .none - - addSubview(stepper) - addSubview(valueLabel) - - addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[v]-[s]-|", options: .alignAllCenterY, metrics: nil, views: ["s": stepper, "v": valueLabel])) - addConstraint(NSLayoutConstraint(item: stepper, attribute: .centerY, relatedBy: .equal, toItem: contentView, attribute: .centerY, multiplier: 1.0, constant: 0)) - addConstraint(NSLayoutConstraint(item: valueLabel, attribute: .centerY, relatedBy: .equal, toItem: stepper, attribute: .centerY, multiplier: 1.0, constant: 0)) - + stepper.addTarget(self, action: #selector(StepperCell.valueChanged), for: .valueChanged) - - valueLabel.textColor = stepper.tintColor } - + deinit { stepper.removeTarget(self, action: nil, for: .allEvents) } - + open override func update() { super.update() stepper.isEnabled = !row.isDisabled stepper.value = row.value ?? 0 stepper.alpha = row.isDisabled ? 0.3 : 1.0 - valueLabel.alpha = row.isDisabled ? 0.3 : 1.0 - valueLabel.text = row.displayValueFor?(row.value) + valueLabel?.textColor = tintColor + valueLabel?.alpha = row.isDisabled ? 0.3 : 1.0 + valueLabel?.text = row.displayValueFor?(row.value) detailTextLabel?.text = nil } - - func valueChanged() { + + @objc func valueChanged() { row.value = stepper.value row.updateCell() } @@ -81,7 +78,6 @@ open class _StepperRow: Row { } } - /// Double row that has a UIStepper as accessoryType public final class StepperRow: _StepperRow, RowType { required public init(tag: String?) { diff --git a/Pods/Eureka/Source/Rows/SwitchRow.swift b/Pods/Eureka/Source/Rows/SwitchRow.swift index 9ccff56..e54f903 100644 --- a/Pods/Eureka/Source/Rows/SwitchRow.swift +++ b/Pods/Eureka/Source/Rows/SwitchRow.swift @@ -26,41 +26,39 @@ import Foundation // MARK: SwitchCell -open class SwitchCell : Cell, CellType { - - public typealias Value = Bool - +open class SwitchCell: Cell, CellType { + + @IBOutlet public weak var switchControl: UISwitch! + required public init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) + let switchC = UISwitch() + switchControl = switchC + accessoryView = switchControl + editingAccessoryView = accessoryView } - + required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } - - open var switchControl: UISwitch? { - return accessoryView as? UISwitch - } - + open override func setup() { super.setup() selectionStyle = .none - accessoryView = UISwitch() - editingAccessoryView = accessoryView - switchControl?.addTarget(self, action: #selector(SwitchCell.valueChanged), for: .valueChanged) + switchControl.addTarget(self, action: #selector(SwitchCell.valueChanged), for: .valueChanged) } - + deinit { switchControl?.removeTarget(self, action: nil, for: .allEvents) } - + open override func update() { super.update() - switchControl?.isOn = row.value ?? false - switchControl?.isEnabled = !row.isDisabled + switchControl.isOn = row.value ?? false + switchControl.isEnabled = !row.isDisabled } - - func valueChanged() { + + @objc func valueChanged() { row.value = switchControl?.isOn ?? false } } @@ -74,7 +72,6 @@ open class _SwitchRow: Row { } } - /// Boolean row that has a UISwitch as accessoryType public final class SwitchRow: _SwitchRow, RowType { required public init(tag: String?) { diff --git a/Pods/Eureka/Source/Rows/TextAreaRow.swift b/Pods/Eureka/Source/Rows/TextAreaRow.swift index ee8dd71..17d6224 100644 --- a/Pods/Eureka/Source/Rows/TextAreaRow.swift +++ b/Pods/Eureka/Source/Rows/TextAreaRow.swift @@ -1,4 +1,4 @@ -// AlertRow.swift +// TextAreaRow.swift // Eureka ( https://github.com/xmartlabs/Eureka ) // // Copyright (c) 2016 Xmartlabs SRL ( http://xmartlabs.com ) @@ -29,17 +29,21 @@ public enum TextAreaHeight { case dynamic(initialTextViewHeight: CGFloat) } -protocol TextAreaConformance: FormatterConformance { - var placeholder : String? { get set } - var textAreaHeight : TextAreaHeight { get set } +public enum TextAreaMode { + case normal + case readOnly } +protocol TextAreaConformance: FormatterConformance { + var placeholder: String? { get set } + var textAreaHeight: TextAreaHeight { get set } +} /** * Protocol for cells that contain a UITextView */ -public protocol AreaCell : TextInputCell { - var textView: UITextView { get } +public protocol AreaCell: TextInputCell { + var textView: UITextView! { get } } extension AreaCell { @@ -49,29 +53,45 @@ extension AreaCell { } open class _TextAreaCell : Cell, UITextViewDelegate, AreaCell where T: Equatable, T: InputTypeInitiable { - - required public init(style: UITableViewCellStyle, reuseIdentifier: String?) { - self.textView = UITextView() - self.textView.translatesAutoresizingMaskIntoConstraints = false - self.placeholderLabel = UILabel() - self.placeholderLabel.translatesAutoresizingMaskIntoConstraints = false - self.placeholderLabel.numberOfLines = 0 - self.placeholderLabel.textColor = UIColor(white: 0, alpha: 0.22) + @IBOutlet public weak var textView: UITextView! + @IBOutlet public weak var placeholderLabel: UILabel? + + private var awakeFromNibCalled = false + + required public init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) + + let textView = UITextView() + self.textView = textView + textView.translatesAutoresizingMaskIntoConstraints = false + textView.keyboardType = .default + textView.font = .preferredFont(forTextStyle: .body) + textView.textContainer.lineFragmentPadding = 0 + textView.textContainerInset = UIEdgeInsets.zero + contentView.addSubview(textView) + + let placeholderLabel = UILabel() + self.placeholderLabel = placeholderLabel + placeholderLabel.translatesAutoresizingMaskIntoConstraints = false + placeholderLabel.numberOfLines = 0 + placeholderLabel.textColor = UIColor(white: 0, alpha: 0.22) + placeholderLabel.font = textView.font + contentView.addSubview(placeholderLabel) } - + required public init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") + super.init(coder: aDecoder) } - - public var placeholderLabel : UILabel - - public var textView: UITextView - + + open override func awakeFromNib() { + super.awakeFromNib() + awakeFromNibCalled = true + } + open var dynamicConstraints = [NSLayoutConstraint]() - + open override func setup() { super.setup() let textAreaRow = row as! TextAreaConformance @@ -82,26 +102,22 @@ open class _TextAreaCell : Cell, UITextViewDelegate, AreaCell where T: Equ case .fixed(let cellHeight): height = { cellHeight } } - textView.keyboardType = .default + textView.delegate = self - textView.font = .preferredFont(forTextStyle: .body) - textView.textContainer.lineFragmentPadding = 0 - textView.textContainerInset = UIEdgeInsets.zero - placeholderLabel.font = textView.font selectionStyle = .none - contentView.addSubview(textView) - contentView.addSubview(placeholderLabel) - - imageView?.addObserver(self, forKeyPath: "image", options: NSKeyValueObservingOptions.old.union(.new), context: nil) + if !awakeFromNibCalled { + imageView?.addObserver(self, forKeyPath: "image", options: NSKeyValueObservingOptions.old.union(.new), context: nil) + } setNeedsUpdateConstraints() } - + deinit { - textView.delegate = nil - imageView?.removeObserver(self, forKeyPath: "image") - + textView?.delegate = nil + if !awakeFromNibCalled { + imageView?.removeObserver(self, forKeyPath: "image") + } } - + open override func update() { super.update() textLabel?.text = nil @@ -109,68 +125,66 @@ open class _TextAreaCell : Cell, UITextViewDelegate, AreaCell where T: Equ textView.isEditable = !row.isDisabled textView.textColor = row.isDisabled ? .gray : .black textView.text = row.displayValueFor?(row.value) - placeholderLabel.text = (row as? TextAreaConformance)?.placeholder - placeholderLabel.sizeToFit() - placeholderLabel.isHidden = textView.text.characters.count != 0 + placeholderLabel?.text = (row as? TextAreaConformance)?.placeholder + placeholderLabel?.isHidden = textView.text.count != 0 } - + open override func cellCanBecomeFirstResponder() -> Bool { - return !row.isDisabled && textView.canBecomeFirstResponder + return !row.isDisabled && textView?.canBecomeFirstResponder == true } - + open override func cellBecomeFirstResponder(withDirection: Direction) -> Bool { // workaround to solve https://github.com/xmartlabs/Eureka/issues/887 UIKit issue - textView.perform(#selector(UITextView.becomeFirstResponder), with: nil, afterDelay: 0.0) + textView?.perform(#selector(UITextView.becomeFirstResponder), with: nil, afterDelay: 0.0) return true - + } - + open override func cellResignFirstResponder() -> Bool { - return textView.resignFirstResponder() + return textView?.resignFirstResponder() ?? true } - + open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { let obj = object as AnyObject? - - if let keyPathValue = keyPath, let changeType = change?[NSKeyValueChangeKey.kindKey], obj === imageView && keyPathValue == "image" && (changeType as? NSNumber)?.uintValue == NSKeyValueChange.setting.rawValue { + + if let keyPathValue = keyPath, let changeType = change?[NSKeyValueChangeKey.kindKey], obj === imageView && keyPathValue == "image" && + (changeType as? NSNumber)?.uintValue == NSKeyValueChange.setting.rawValue, !awakeFromNibCalled { setNeedsUpdateConstraints() updateConstraintsIfNeeded() } } - + //Mark: Helpers - + private func displayValue(useFormatter: Bool) -> String? { guard let v = row.value else { return nil } if let formatter = (row as? FormatterConformance)?.formatter, useFormatter { - return textView.isFirstResponder ? formatter.editingString(for: v) : formatter.string(for: v) + return textView?.isFirstResponder == true ? formatter.editingString(for: v) : formatter.string(for: v) } return String(describing: v) } - - //MARK: TextFieldDelegate - - + + // MARK: TextFieldDelegate + open func textViewDidBeginEditing(_ textView: UITextView) { formViewController()?.beginEditing(of: self) formViewController()?.textInputDidBeginEditing(textView, cell: self) if let textAreaConformance = (row as? TextAreaConformance), let _ = textAreaConformance.formatter, textAreaConformance.useFormatterOnDidBeginEditing ?? textAreaConformance.useFormatterDuringInput { textView.text = self.displayValue(useFormatter: true) - } - else { + } else { textView.text = self.displayValue(useFormatter: false) } } - + open func textViewDidEndEditing(_ textView: UITextView) { formViewController()?.endEditing(of: self) formViewController()?.textInputDidEndEditing(textView, cell: self) textViewDidChange(textView) textView.text = displayValue(useFormatter: (row as? FormatterConformance)?.formatter != nil) } - + open func textViewDidChange(_ textView: UITextView) { - + if let textAreaConformance = row as? TextAreaConformance, case .dynamic = textAreaConformance.textAreaHeight, let tableView = formViewController()?.tableView { let currentOffset = tableView.contentOffset UIView.setAnimationsEnabled(false) @@ -179,7 +193,7 @@ open class _TextAreaCell : Cell, UITextViewDelegate, AreaCell where T: Equ UIView.setAnimationsEnabled(true) tableView.setContentOffset(currentOffset, animated: false) } - placeholderLabel.isHidden = textView.text.characters.count != 0 + placeholderLabel?.isHidden = textView.text.count != 0 guard let textValue = textView.text else { row.value = nil return @@ -200,8 +214,7 @@ open class _TextAreaCell : Cell, UITextViewDelegate, AreaCell where T: Equ textView.selectedTextRange = textView.textRange(from: selStartPos, to: selStartPos) return } - } - else { + } else { let value: AutoreleasingUnsafeMutablePointer = AutoreleasingUnsafeMutablePointer.init(UnsafeMutablePointer.allocate(capacity: 1)) let errorDesc: AutoreleasingUnsafeMutablePointer? = nil if formatter.getObjectValue(value, for: textValue, errorDescription: errorDesc) { @@ -209,64 +222,68 @@ open class _TextAreaCell : Cell, UITextViewDelegate, AreaCell where T: Equ } } } - + open func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { return formViewController()?.textInput(textView, shouldChangeCharactersInRange: range, replacementString: text, cell: self) ?? true } - + open func textViewShouldBeginEditing(_ textView: UITextView) -> Bool { + if let textAreaRow = self.row as? _TextAreaRow, textAreaRow.textAreaMode == .readOnly { + return false + } return formViewController()?.textInputShouldBeginEditing(textView, cell: self) ?? true } - + open func textViewShouldEndEditing(_ textView: UITextView) -> Bool { return formViewController()?.textInputShouldEndEditing(textView, cell: self) ?? true } - - open override func updateConstraints(){ + + open override func updateConstraints() { customConstraints() super.updateConstraints() } - + open func customConstraints() { + guard !awakeFromNibCalled else { return } + contentView.removeConstraints(dynamicConstraints) dynamicConstraints = [] - var views : [String: AnyObject] = ["textView": textView, "label": placeholderLabel] + var views: [String: AnyObject] = ["textView": textView, "label": placeholderLabel!] dynamicConstraints.append(contentsOf: NSLayoutConstraint.constraints(withVisualFormat: "V:|-[label]", options: [], metrics: nil, views: views)) if let textAreaConformance = row as? TextAreaConformance, case .dynamic(let initialTextViewHeight) = textAreaConformance.textAreaHeight { dynamicConstraints.append(contentsOf: NSLayoutConstraint.constraints(withVisualFormat: "V:|-[textView(>=initialHeight@800)]-|", options: [], metrics: ["initialHeight": initialTextViewHeight], views: views)) - } - else { + } else { dynamicConstraints.append(contentsOf: NSLayoutConstraint.constraints(withVisualFormat: "V:|-[textView]-|", options: [], metrics: nil, views: views)) } if let imageView = imageView, let _ = imageView.image { views["imageView"] = imageView dynamicConstraints.append(contentsOf: NSLayoutConstraint.constraints(withVisualFormat: "H:[imageView]-(15)-[textView]-|", options: [], metrics: nil, views: views)) dynamicConstraints.append(contentsOf: NSLayoutConstraint.constraints(withVisualFormat: "H:[imageView]-(15)-[label]-|", options: [], metrics: nil, views: views)) - } - else { + } else { dynamicConstraints.append(contentsOf: NSLayoutConstraint.constraints(withVisualFormat: "H:|-[textView]-|", options: [], metrics: nil, views: views)) dynamicConstraints.append(contentsOf: NSLayoutConstraint.constraints(withVisualFormat: "H:|-[label]-|", options: [], metrics: nil, views: views)) } contentView.addConstraints(dynamicConstraints) } - + } -open class TextAreaCell : _TextAreaCell, CellType { - +open class TextAreaCell: _TextAreaCell, CellType { + required public init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) } - + required public init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") + super.init(coder: aDecoder) } } open class AreaRow: FormatteableRow, TextAreaConformance where Cell: BaseCell, Cell: AreaCell { - - open var placeholder : String? + + open var placeholder: String? open var textAreaHeight = TextAreaHeight.fixed(cellHeight: 110) + open var textAreaMode = TextAreaMode.normal public required init(tag: String?) { super.init(tag: tag) @@ -285,5 +302,3 @@ public final class TextAreaRow: _TextAreaRow, RowType { super.init(tag: tag) } } - - diff --git a/Pods/Eureka/Source/Rows/TriplePickerInputRow.swift b/Pods/Eureka/Source/Rows/TriplePickerInputRow.swift new file mode 100644 index 0000000..97c3b38 --- /dev/null +++ b/Pods/Eureka/Source/Rows/TriplePickerInputRow.swift @@ -0,0 +1,157 @@ +// +// TriplePickerInputRow.swift +// Eureka +// +// Created by Mathias Claassen on 5/10/18. +// Copyright © 2018 Xmartlabs. All rights reserved. +// + +import Foundation + +open class TriplePickerInputCell : _PickerInputCell> where A: Equatable, B: Equatable, C: Equatable { + + private var pickerRow: _TriplePickerInputRow! { return row as? _TriplePickerInputRow } + + public required init(style: UITableViewCellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + open override func update() { + super.update() + if let selectedValue = pickerRow.value, let indexA = pickerRow.firstOptions().index(of: selectedValue.a), + let indexB = pickerRow.secondOptions(selectedValue.a).index(of: selectedValue.b), + let indexC = pickerRow.thirdOptions(selectedValue.a, selectedValue.b).index(of: selectedValue.c){ + picker.selectRow(indexA, inComponent: 0, animated: true) + picker.selectRow(indexB, inComponent: 1, animated: true) + picker.selectRow(indexC, inComponent: 2, animated: true) + } + } + + open override func numberOfComponents(in pickerView: UIPickerView) -> Int { + return 3 + } + + open override func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { + if component == 0 { + return pickerRow.firstOptions().count + } else if component == 1 { + return pickerRow.secondOptions(pickerRow.selectedFirst()).count + } else { + return pickerRow.thirdOptions(pickerRow.selectedFirst(), pickerRow.selectedSecond()).count + } + } + + open override func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { + if component == 0 { + return pickerRow.displayValueForFirstRow(pickerRow.firstOptions()[row]) + } else if component == 1 { + return pickerRow.displayValueForSecondRow(pickerRow.secondOptions(pickerRow.selectedFirst())[row]) + } else { + return pickerRow.displayValueForThirdRow(pickerRow.thirdOptions(pickerRow.selectedFirst(), pickerRow.selectedSecond())[row]) + } + } + + open override func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { + if component == 0 { + let a = pickerRow.firstOptions()[row] + if let value = pickerRow.value { + guard value.a != a else { + return + } + + let b: B = pickerRow.secondOptions(a).contains(value.b) ? value.b : pickerRow.secondOptions(a)[0] + let c: C = pickerRow.thirdOptions(a, b).contains(value.c) ? value.c : pickerRow.thirdOptions(a, b)[0] + pickerView.reloadComponent(1) + pickerView.reloadComponent(2) + pickerRow.value = Tuple3(a: a, b: b, c: c) + if b != value.b { + pickerView.selectRow(0, inComponent: 1, animated: true) + } + if c != value.c { + pickerView.selectRow(0, inComponent: 2, animated: true) + } + } else { + let b = pickerRow.secondOptions(a)[0] + pickerRow.value = Tuple3(a: a, b: b, c: pickerRow.thirdOptions(a, b)[0]) + pickerView.reloadComponent(1) + pickerView.reloadComponent(2) + pickerView.selectRow(0, inComponent: 1, animated: true) + pickerView.selectRow(0, inComponent: 2, animated: true) + } + } else if component == 1 { + let a = pickerRow.selectedFirst() + let b = pickerRow.secondOptions(a)[row] + if let value = pickerRow.value { + guard value.b != b else { + return + } + if pickerRow.thirdOptions(a, b).contains(value.c) { + pickerRow.value = Tuple3(a: a, b: b, c: value.c) + pickerView.reloadComponent(2) + update() + return + } else { + pickerRow.value = Tuple3(a: a, b: b, c: pickerRow.thirdOptions(a, b)[0]) + } + } else { + pickerRow.value = Tuple3(a: a, b: b, c: pickerRow.thirdOptions(a, b)[0]) + } + pickerView.reloadComponent(2) + pickerView.selectRow(0, inComponent: 2, animated: true) + } else { + let a = pickerRow.selectedFirst() + let b = pickerRow.selectedSecond() + pickerRow.value = Tuple3(a: a, b: b, c: pickerRow.thirdOptions(a, b)[row]) + } + update() + } +} + +open class _TriplePickerInputRow : Row>, NoValueDisplayTextConformance { + + open var noValueDisplayText: String? = nil + /// Options for first component. Will be called often so should be O(1) + public var firstOptions: (() -> [A]) = {[]} + /// Options for second component given the selected value from the first component. Will be called often so should be O(1) + public var secondOptions: ((A) -> [B]) = {_ in []} + /// Options for third component given the selected value from the first and second components. Will be called often so should be O(1) + public var thirdOptions: ((A, B) -> [C]) = {_, _ in []} + + /// Modify the displayed values for the first picker row. + public var displayValueForFirstRow: ((A) -> (String)) = { a in return String(describing: a) } + /// Modify the displayed values for the second picker row. + public var displayValueForSecondRow: ((B) -> (String)) = { b in return String(describing: b) } + /// Modify the displayed values for the third picker row. + public var displayValueForThirdRow: ((C) -> (String)) = { c in return String(describing: c) } + + required public init(tag: String?) { + super.init(tag: tag) + } + + func selectedFirst() -> A { + return value?.a ?? firstOptions()[0] + } + + func selectedSecond() -> B { + return value?.b ?? secondOptions(selectedFirst())[0] + } + +} + +/// A generic row where the user can pick an option from a picker view displayed in the keyboard area +public final class TriplePickerInputRow: _TriplePickerInputRow, RowType where A: Equatable, B: Equatable, C: Equatable { + + required public init(tag: String?) { + super.init(tag: tag) + self.displayValueFor = { [weak self] tuple in + guard let tuple = tuple else { + return self?.noValueDisplayText + } + return String(describing: tuple.a) + ", " + String(describing: tuple.b) + ", " + String(describing: tuple.c) + } + } +} diff --git a/Pods/Eureka/Source/Rows/TriplePickerRow.swift b/Pods/Eureka/Source/Rows/TriplePickerRow.swift new file mode 100644 index 0000000..f7474a8 --- /dev/null +++ b/Pods/Eureka/Source/Rows/TriplePickerRow.swift @@ -0,0 +1,172 @@ +// +// TriplePickerRow.swift +// Eureka +// +// Created by Mathias Claassen on 5/9/18. +// Copyright © 2018 Xmartlabs. All rights reserved. +// + +import Foundation + +public struct Tuple3 { + public let a: A + public let b: B + public let c: C + + public init(a: A, b: B, c: C) { + self.a = a + self.b = b + self.c = c + } + +} + +extension Tuple3: Equatable {} + +public func == (lhs: Tuple3, rhs: Tuple3) -> Bool { + return lhs.a == rhs.a && lhs.b == rhs.b && lhs.c == rhs.c +} + +// MARK: MultiplePickerCell + +open class TriplePickerCell : _PickerCell> where A: Equatable, B: Equatable, C: Equatable { + + private var pickerRow: _TriplePickerRow! { return row as? _TriplePickerRow } + + public required init(style: UITableViewCellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + open override func update() { + super.update() + if let selectedValue = pickerRow.value, let indexA = pickerRow.firstOptions().index(of: selectedValue.a), + let indexB = pickerRow.secondOptions(selectedValue.a).index(of: selectedValue.b), + let indexC = pickerRow.thirdOptions(selectedValue.a, selectedValue.b).index(of: selectedValue.c) { + picker.selectRow(indexA, inComponent: 0, animated: true) + picker.selectRow(indexB, inComponent: 1, animated: true) + picker.selectRow(indexC, inComponent: 2, animated: true) + } + } + + open override func numberOfComponents(in pickerView: UIPickerView) -> Int { + return 3 + } + + open override func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { + if component == 0 { + return pickerRow.firstOptions().count + } else if component == 1 { + return pickerRow.secondOptions(pickerRow.selectedFirst()).count + } else { + return pickerRow.thirdOptions(pickerRow.selectedFirst(), pickerRow.selectedSecond()).count + } + } + + open override func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { + if component == 0 { + return pickerRow.displayValueForFirstRow(pickerRow.firstOptions()[row]) + } else if component == 1 { + return pickerRow.displayValueForSecondRow(pickerRow.secondOptions(pickerRow.selectedFirst())[row]) + } else { + return pickerRow.displayValueForThirdRow(pickerRow.thirdOptions(pickerRow.selectedFirst(), pickerRow.selectedSecond())[row]) + } + } + + open override func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { + if component == 0 { + let a = pickerRow.firstOptions()[row] + if let value = pickerRow.value { + guard value.a != a else { + return + } + + let b: B = pickerRow.secondOptions(a).contains(value.b) ? value.b : pickerRow.secondOptions(a)[0] + let c: C = pickerRow.thirdOptions(a, b).contains(value.c) ? value.c : pickerRow.thirdOptions(a, b)[0] + pickerView.reloadComponent(1) + pickerView.reloadComponent(2) + pickerRow.value = Tuple3(a: a, b: b, c: c) + if b != value.b { + pickerView.selectRow(0, inComponent: 1, animated: true) + } + if c != value.c { + pickerView.selectRow(0, inComponent: 2, animated: true) + } + } else { + let b = pickerRow.secondOptions(a)[0] + pickerRow.value = Tuple3(a: a, b: b, c: pickerRow.thirdOptions(a, b)[0]) + pickerView.reloadComponent(1) + pickerView.reloadComponent(2) + pickerView.selectRow(0, inComponent: 1, animated: true) + pickerView.selectRow(0, inComponent: 2, animated: true) + } + } else if component == 1 { + let a = pickerRow.selectedFirst() + let b = pickerRow.secondOptions(a)[row] + if let value = pickerRow.value { + guard value.b != b else { + return + } + if pickerRow.thirdOptions(a, b).contains(value.c) { + pickerRow.value = Tuple3(a: a, b: b, c: value.c) + pickerView.reloadComponent(2) + return + } else { + pickerRow.value = Tuple3(a: a, b: b, c: pickerRow.thirdOptions(a, b)[0]) + } + } else { + pickerRow.value = Tuple3(a: a, b: b, c: pickerRow.thirdOptions(a, b)[0]) + } + pickerView.reloadComponent(2) + pickerView.selectRow(0, inComponent: 2, animated: true) + } else { + let a = pickerRow.selectedFirst() + let b = pickerRow.selectedSecond() + pickerRow.value = Tuple3(a: a, b: b, c: pickerRow.thirdOptions(a, b)[row]) + } + } + +} + +// MARK: PickerRow +open class _TriplePickerRow : Row> where A: Equatable, B: Equatable, C: Equatable { + + /// Options for first component. Will be called often so should be O(1) + public var firstOptions: (() -> [A]) = {[]} + /// Options for second component given the selected value from the first component. Will be called often so should be O(1) + public var secondOptions: ((A) -> [B]) = {_ in []} + /// Options for third component given the selected value from the first and second components. Will be called often so should be O(1) + public var thirdOptions: ((A, B) -> [C]) = {_, _ in []} + + /// Modify the displayed values for the first picker row. + public var displayValueForFirstRow: ((A) -> (String)) = { a in return String(describing: a) } + /// Modify the displayed values for the second picker row. + public var displayValueForSecondRow: ((B) -> (String)) = { b in return String(describing: b) } + /// Modify the displayed values for the third picker row. + public var displayValueForThirdRow: ((C) -> (String)) = { c in return String(describing: c) } + + required public init(tag: String?) { + super.init(tag: tag) + } + + func selectedFirst() -> A { + return value?.a ?? firstOptions()[0] + } + + func selectedSecond() -> B { + return value?.b ?? secondOptions(selectedFirst())[0] + } + +} + +/// A generic row where the user can pick an option from a picker view +public final class TriplePickerRow: _TriplePickerRow, RowType where A: Equatable, B: Equatable, C: Equatable { + + required public init(tag: String?) { + super.init(tag: tag) + } + +} diff --git a/Pods/Eureka/Source/Validations/RuleClosure.swift b/Pods/Eureka/Source/Validations/RuleClosure.swift index afb306f..e5f5b36 100644 --- a/Pods/Eureka/Source/Validations/RuleClosure.swift +++ b/Pods/Eureka/Source/Validations/RuleClosure.swift @@ -24,20 +24,20 @@ import Foundation - public struct RuleClosure: RuleType { - + public var id: String? public var validationError: ValidationError - + public var closure: (T?) -> ValidationError? public func isValid(value: T?) -> ValidationError? { return closure(value) } - - public init(validationError: ValidationError = ValidationError(msg: "Field validation fails.."), closure: @escaping ((T?) -> ValidationError?)) { + + public init(validationError: ValidationError = ValidationError(msg: "Field validation fails.."), id: String? = nil, closure: @escaping ((T?) -> ValidationError?)) { self.validationError = validationError self.closure = closure + self.id = id } } diff --git a/Pods/Eureka/Source/Validations/RuleEmail.swift b/Pods/Eureka/Source/Validations/RuleEmail.swift index a24c77f..87d7737 100644 --- a/Pods/Eureka/Source/Validations/RuleEmail.swift +++ b/Pods/Eureka/Source/Validations/RuleEmail.swift @@ -25,9 +25,9 @@ import Foundation public class RuleEmail: RuleRegExp { - - public init(msg: String = "Field value should be a valid email!") { - super.init(regExpr: RegExprPattern.EmailAddress.rawValue, allowsEmpty: true, msg: msg) + + public init(msg: String = "Field value should be a valid email!", id: String? = nil) { + super.init(regExpr: RegExprPattern.EmailAddress.rawValue, allowsEmpty: true, msg: msg, id: id) } - + } diff --git a/Pods/Eureka/Source/Validations/RuleEqualsToRow.swift b/Pods/Eureka/Source/Validations/RuleEqualsToRow.swift index b508b2f..bcabd9b 100644 --- a/Pods/Eureka/Source/Validations/RuleEqualsToRow.swift +++ b/Pods/Eureka/Source/Validations/RuleEqualsToRow.swift @@ -25,27 +25,29 @@ import Foundation public struct RuleEqualsToRow: RuleType { - - public init(form: Form, tag: String, msg: String = "Fields don't match!"){ + + public init(form: Form, tag: String, msg: String = "Fields don't match!", id: String? = nil) { self.validationError = ValidationError(msg: msg) self.form = form self.tag = tag - self.row = nil; + self.row = nil + self.id = id } - - public init(row: RowOf, msg: String = "Fields don't match!"){ + + public init(row: RowOf, msg: String = "Fields don't match!", id: String? = nil) { self.validationError = ValidationError(msg: msg) self.form = nil self.tag = nil self.row = row + self.id = id } - + public var id: String? public var validationError: ValidationError - public var form: Form? + public weak var form: Form? public var tag: String? - public var row: RowOf? - + public weak var row: RowOf? + public func isValid(value: T?) -> ValidationError? { let rowAux: RowOf = row ?? form!.rowBy(tag: tag!)! return rowAux.value == value ? nil : validationError diff --git a/Pods/Eureka/Source/Validations/RuleLength.swift b/Pods/Eureka/Source/Validations/RuleLength.swift index 0824862..87e2c75 100644 --- a/Pods/Eureka/Source/Validations/RuleLength.swift +++ b/Pods/Eureka/Source/Validations/RuleLength.swift @@ -24,41 +24,61 @@ import Foundation - public struct RuleMinLength: RuleType { - + let min: UInt - + public var id: String? public var validationError: ValidationError - - public init(minLength: UInt, msg: String? = nil){ + + public init(minLength: UInt, msg: String? = nil, id: String? = nil) { let ruleMsg = msg ?? "Field value must have at least \(minLength) characters" min = minLength validationError = ValidationError(msg: ruleMsg) + self.id = id } - + public func isValid(value: String?) -> ValidationError? { guard let value = value else { return nil } - return value.characters.count < Int(min) ? validationError : nil + return value.count < Int(min) ? validationError : nil } } public struct RuleMaxLength: RuleType { - + let max: UInt - + public var id: String? public var validationError: ValidationError - - public init(maxLength: UInt, msg: String? = nil){ + + public init(maxLength: UInt, msg: String? = nil, id: String? = nil) { let ruleMsg = msg ?? "Field value must have less than \(maxLength) characters" max = maxLength validationError = ValidationError(msg: ruleMsg) + self.id = id + } + + public func isValid(value: String?) -> ValidationError? { + guard let value = value else { return nil } + return value.count > Int(max) ? validationError : nil + } +} + +public struct RuleExactLength: RuleType { + let length: UInt + + public var id: String? + public var validationError: ValidationError + + public init(exactLength: UInt, msg: String? = nil, id: String? = nil) { + let ruleMsg = msg ?? "Field value must have exactly \(exactLength) characters" + length = exactLength + validationError = ValidationError(msg: ruleMsg) + self.id = id } public func isValid(value: String?) -> ValidationError? { guard let value = value else { return nil } - return value.characters.count > Int(max) ? validationError : nil + return value.count != Int(length) ? validationError : nil } } diff --git a/Pods/Eureka/Source/Validations/RuleRange.swift b/Pods/Eureka/Source/Validations/RuleRange.swift index daa3fdb..92da589 100644 --- a/Pods/Eureka/Source/Validations/RuleRange.swift +++ b/Pods/Eureka/Source/Validations/RuleRange.swift @@ -25,18 +25,19 @@ import Foundation public struct RuleGreaterThan: RuleType { - + let min: T - + public var id: String? public var validationError: ValidationError - - public init(min: T, msg: String? = nil){ + + public init(min: T, msg: String? = nil, id: String? = nil) { let ruleMsg = msg ?? "Field value must be greater than \(min)" self.min = min self.validationError = ValidationError(msg: ruleMsg) + self.id = id } - + public func isValid(value: T?) -> ValidationError? { guard let val = value else { return nil } guard val > min else { return validationError } @@ -45,18 +46,19 @@ public struct RuleGreaterThan: RuleType { } public struct RuleGreaterOrEqualThan: RuleType { - + let min: T - + public var id: String? public var validationError: ValidationError - - public init(min: T, msg: String? = nil){ + + public init(min: T, msg: String? = nil, id: String? = nil) { let ruleMsg = msg ?? "Field value must be greater or equals than \(min)" self.min = min self.validationError = ValidationError(msg: ruleMsg) + self.id = id } - + public func isValid(value: T?) -> ValidationError? { guard let val = value else { return nil } guard val >= min else { return validationError } @@ -65,18 +67,19 @@ public struct RuleGreaterOrEqualThan: RuleType { } public struct RuleSmallerThan: RuleType { - + let max: T - + public var id: String? public var validationError: ValidationError - - public init(max: T, msg: String? = nil) { + + public init(max: T, msg: String? = nil, id: String? = nil) { let ruleMsg = msg ?? "Field value must be smaller than \(max)" self.max = max self.validationError = ValidationError(msg: ruleMsg) + self.id = id } - + public func isValid(value: T?) -> ValidationError? { guard let val = value else { return nil } guard val < max else { return validationError } @@ -85,18 +88,19 @@ public struct RuleSmallerThan: RuleType { } public struct RuleSmallerOrEqualThan: RuleType { - + let max: T - + public var id: String? public var validationError: ValidationError - - public init(max: T, msg: String? = nil) { + + public init(max: T, msg: String? = nil, id: String? = nil) { let ruleMsg = msg ?? "Field value must be smaller or equals than \(max)" self.max = max self.validationError = ValidationError(msg: ruleMsg) + self.id = id } - + public func isValid(value: T?) -> ValidationError? { guard let val = value else { return nil } guard val <= max else { return validationError } diff --git a/Pods/Eureka/Source/Validations/RuleRegExp.swift b/Pods/Eureka/Source/Validations/RuleRegExp.swift index e9d238b..6c44162 100644 --- a/Pods/Eureka/Source/Validations/RuleRegExp.swift +++ b/Pods/Eureka/Source/Validations/RuleRegExp.swift @@ -26,7 +26,7 @@ import Foundation public enum RegExprPattern: String { case EmailAddress = "^[_A-Za-z0-9-+]+(\\.[_A-Za-z0-9-+]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z‌​]{2,})$" - case URL = "((https|http)://)((\\w|-)+)(([.]|[/])((\\w|-)+))+" + case URL = "((https|http)://)((\\w|-)+)(([.]|[/])((\\w|-)+))+([/?#]\\S*)?" case ContainsNumber = ".*\\d.*" case ContainsCapital = "^.*?[A-Z].*?$" case ContainsLowercase = "^.*?[a-z].*?$" @@ -38,22 +38,22 @@ open class RuleRegExp: RuleType { public var id: String? public var validationError: ValidationError public var allowsEmpty = true - - public init(regExpr: String, allowsEmpty: Bool = true, msg: String = "Invalid field value!"){ + + public init(regExpr: String, allowsEmpty: Bool = true, msg: String = "Invalid field value!", id: String? = nil) { self.validationError = ValidationError(msg: msg) self.regExpr = regExpr self.allowsEmpty = allowsEmpty + self.id = id } - + public func isValid(value: String?) -> ValidationError? { - if let value = value, !value.isEmpty{ + if let value = value, !value.isEmpty { let predicate = NSPredicate(format: "SELF MATCHES %@", regExpr) guard predicate.evaluate(with: value) else { return validationError } return nil - } - else if !allowsEmpty { + } else if !allowsEmpty { return validationError } return nil diff --git a/Pods/Eureka/Source/Validations/RuleRequired.swift b/Pods/Eureka/Source/Validations/RuleRequired.swift index cfc7412..390fc1a 100644 --- a/Pods/Eureka/Source/Validations/RuleRequired.swift +++ b/Pods/Eureka/Source/Validations/RuleRequired.swift @@ -25,14 +25,15 @@ import Foundation public struct RuleRequired: RuleType { - - public init(msg: String = "Field required!"){ + + public init(msg: String = "Field required!", id: String? = nil) { self.validationError = ValidationError(msg: msg) + self.id = id } - + public var id: String? public var validationError: ValidationError - + public func isValid(value: T?) -> ValidationError? { if let str = value as? String { return str.isEmpty ? validationError : nil diff --git a/Pods/Eureka/Source/Validations/RuleURL.swift b/Pods/Eureka/Source/Validations/RuleURL.swift index 866371d..8c5f31d 100644 --- a/Pods/Eureka/Source/Validations/RuleURL.swift +++ b/Pods/Eureka/Source/Validations/RuleURL.swift @@ -26,16 +26,19 @@ import Foundation import UIKit public struct RuleURL: RuleType { - - public init(allowsEmpty: Bool = true, requiresProtocol: Bool = false, msg: String = "Field value must be an URL!") { + + public init(allowsEmpty: Bool = true, requiresProtocol: Bool = false, msg: String = "Field value must be an URL!", id: String? = nil) { validationError = ValidationError(msg: msg) + self.allowsEmpty = allowsEmpty + self.requiresProtocol = requiresProtocol + self.id = id } - + public var id: String? public var allowsEmpty = true public var requiresProtocol = false public var validationError: ValidationError - + public func isValid(value: URL?) -> ValidationError? { if let value = value, value.absoluteString.isEmpty == false { let predicate = NSPredicate(format:"SELF MATCHES %@", RegExprPattern.URL.rawValue) @@ -43,8 +46,7 @@ public struct RuleURL: RuleType { return validationError } return nil - } - else if !allowsEmpty { + } else if !allowsEmpty { return validationError } return nil diff --git a/Pods/Manifest.lock b/Pods/Manifest.lock index 0581254..e32ff83 100644 --- a/Pods/Manifest.lock +++ b/Pods/Manifest.lock @@ -1,26 +1,34 @@ PODS: - - Alamofire (4.4.0) - - Eureka (2.0.1) - - Realm (2.4.3): - - Realm/Headers (= 2.4.3) - - Realm/Headers (2.4.3) - - RealmSwift (2.4.3): - - Realm (= 2.4.3) - - SwiftyJSON (3.1.4) + - Alamofire (4.7.3) + - Eureka (4.2.0) + - Realm (3.7.6): + - Realm/Headers (= 3.7.6) + - Realm/Headers (3.7.6) + - RealmSwift (3.7.6): + - Realm (= 3.7.6) + - SwiftyJSON (4.1.0) DEPENDENCIES: - Alamofire - - Eureka (~> 2.0) + - Eureka (~> 4.2.0) - RealmSwift - SwiftyJSON +SPEC REPOS: + https://github.com/CocoaPods/Specs.git: + - Alamofire + - Eureka + - Realm + - RealmSwift + - SwiftyJSON + SPEC CHECKSUMS: - Alamofire: dc44b1600b800eb63da6a19039a0083d62a6a62d - Eureka: ae837bb1dfae0c07bc1bda7aeef87876eeb537c2 - Realm: e08d60b0048ccccd3949c8c28a92c4db5712d7e9 - RealmSwift: 30d49739ccb3385d201242736ca4dda56aec9dfc - SwiftyJSON: c2842d878f95482ffceec5709abc3d05680c0220 + Alamofire: c7287b6e5d7da964a70935e5db17046b7fde6568 + Eureka: 0748c7bbb2560130e43c7bfa83e99c98854a1f42 + Realm: 9eaecad54712d6246d08ba34c10f354e4715d7d3 + RealmSwift: 1fe08b4ebaeeaacf17f9ba0343f7b25c9fc31fa6 + SwiftyJSON: c29297daf073d2aa016295d5809cdd68045c39b3 -PODFILE CHECKSUM: 8d33131f567d5e8d57a6d7d0d5ded13deac16030 +PODFILE CHECKSUM: ad2c1cd3d3c073e25f16eef71b60c2ff816f6511 -COCOAPODS: 1.2.0.beta.1 +COCOAPODS: 1.5.0 diff --git a/Pods/Pods.xcodeproj/project.pbxproj b/Pods/Pods.xcodeproj/project.pbxproj index ed99a71..45c8e1b 100644 --- a/Pods/Pods.xcodeproj/project.pbxproj +++ b/Pods/Pods.xcodeproj/project.pbxproj @@ -7,290 +7,302 @@ objects = { /* Begin PBXBuildFile section */ - 02F07D843F3B3151880D1E67205C11A0 /* Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F7E0ADF17EBC958B251815CC7568CDA /* Core.swift */; }; - 04292A24DCB888A874716AED5A0991B5 /* RLMSyncPermissionChange.h in Headers */ = {isa = PBXBuildFile; fileRef = DD4813B305768312DFCC46E17B165E9D /* RLMSyncPermissionChange.h */; }; - 049C5680FBE95156A76473F565839866 /* object_schema.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E9A4F74F141D09D7CD5998FAA9A1870D /* object_schema.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 04A9A536DFAF3C5EC723F92EED127215 /* SegmentedRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D950242A064EF6D387D762AC469B49A /* SegmentedRow.swift */; }; - 06B76D890CEA891B7CC1502670DA4747 /* RLMSyncPermissionOffer.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 9905EB2DBE88423775498C0762231D31 /* RLMSyncPermissionOffer.h */; }; - 08450EB04D43DD17968CCBCFDD1CD605 /* RLMRealmConfiguration_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 24CA718B9D06B3DE1F62BB0C374BE311 /* RLMRealmConfiguration_Private.h */; }; - 09C5C995F970A0405FCA6CB8EFAB30C5 /* RLMAccessor.mm in Sources */ = {isa = PBXBuildFile; fileRef = BE393BF220453660A254404E054F0FAD /* RLMAccessor.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 0AD4C6175BDD69CE078B71611E208EE5 /* RLMUpdateChecker.mm in Sources */ = {isa = PBXBuildFile; fileRef = BEE43652C132B102F2A932B2491BD544 /* RLMUpdateChecker.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 0B2FCAF300C1B74DA6343E48B7D5F3F6 /* PickerInlineRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D0589AF94C2F182098FF711C58BE901 /* PickerInlineRow.swift */; }; - 0C19B0ADBDDF9D1F35443473DD32F58C /* Form.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2EF20A785AA2BDD2D0C2BF1D987A6DB /* Form.swift */; }; - 0C6AC71BA3BD053BE367C59005B75436 /* RLMSwiftSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A0DD02D17A83F4D2E50F8FCFE01B983 /* RLMSwiftSupport.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 0F47F6ABDFD61B71965C06B833435E53 /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA60E1705211357CF6C402D21D847134 /* Protocols.swift */; }; - 101FAFF668DB8CBB0AD05263D1314761 /* sync_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2719B5CBD57B584D6C0A839CFCC22254 /* sync_file.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 10EB23E9ECC4B33E16933BB1EA560B6A /* Timeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59BF4F23201A68D17489D807C9E924B3 /* Timeline.swift */; }; - 12AADCB1FDC69E010AB5EE2322EB0ED5 /* RLMSyncErrorResponseModel.m in Sources */ = {isa = PBXBuildFile; fileRef = D6D779DD4034A34EB92387F98B71760D /* RLMSyncErrorResponseModel.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 144A795542A55FCA27F839FD8B3848AD /* CellType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C65356B61466B850C0C42E1D84A3104 /* CellType.swift */; }; - 15103A8FD65EA7CAE8BBF5B3B0EF9905 /* Results.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D89CAA2E126C17A26758614230011E6 /* Results.swift */; }; - 15B32D07613721F00FBBDDACBC04B140 /* weak_realm_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4E4498D0A2C0E7E34E8068D4254AECF6 /* weak_realm_notifier.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 1659B1265BBB42366D2413083AF0129A /* RLMResults.h in Headers */ = {isa = PBXBuildFile; fileRef = 18C1C43D04AF90570D89DF2922616B6E /* RLMResults.h */; }; - 17048D78835182A0B4E9D7926DA75DAD /* PopoverSelectorRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25E5133FA249E54193DCD306DB40A7A0 /* PopoverSelectorRow.swift */; }; - 1727C54D0D94CE7C475585ED927DB6F1 /* RLMSyncUtil_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 6C05CDAFA31C3E20FFA2BA6425E1CEBF /* RLMSyncUtil_Private.h */; }; - 1772EAFC71C30BD931A9585212CA03AC /* Row.swift in Sources */ = {isa = PBXBuildFile; fileRef = F587B0BB1761780DA10D5C25BFAB5DDC /* Row.swift */; }; - 17A3FB1B471D1C1A61BCFBBF5831D845 /* RowControllerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE125D3EAB2AD7398DA1E3D3A7267498 /* RowControllerType.swift */; }; - 184852455BA8F838459DBEBD6D6C8D84 /* RLMConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = AC10336CC1E93B42D3F920ED569F5259 /* RLMConstants.h */; }; - 192BE4C9EFA0640AECD520063F1BC7C9 /* RLMSyncUser.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 502995188A080E6D6436BF64DAC9E5B4 /* RLMSyncUser.h */; }; - 1A8BC03AFD56C7EA1656DC1D828FD2C7 /* RLMRealmConfiguration.mm in Sources */ = {isa = PBXBuildFile; fileRef = 64B6D07C5164ED664F551EBD8B081CE7 /* RLMRealmConfiguration.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 1A98452905C05D17890F5A565091D38D /* RLMQueryUtil.mm in Sources */ = {isa = PBXBuildFile; fileRef = EF7DE4D98DFB45FBCEAA827C87595DE6 /* RLMQueryUtil.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 1ACA00A9EC125081B88B9228462431B8 /* ButtonRowWithPresent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F3E40991A37DCB4879656F07CC9B31 /* ButtonRowWithPresent.swift */; }; - 1ADD9667E804CB33EED9B0C2F25FCDB2 /* Object.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72C33C05F0CC7EEDF1F38905DDE92572 /* Object.swift */; }; - 1B9EDEDC964E6B08F78920B4F4B9DB84 /* Alamofire-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = B011D58D42FE700BD7FBE2FA010DA61B /* Alamofire-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 1C5B941F79B39828B27EAAD3C9AF7B4B /* PickerRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D0CE63E3233460D4B17968656664499 /* PickerRow.swift */; }; - 1D4CD70DC32B037FCD6320FE079695D5 /* CheckRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 883CEA612E14A5F05417D6AD6B3297BF /* CheckRow.swift */; }; - 1D996DF963ACA6E9879D2B57CCBF9490 /* RLMSyncPermissionOfferResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E829C2D738568B2DD7488FB084BD4EC1 /* RLMSyncPermissionOfferResponse.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 1DC99B171D97F8B7916652C011712318 /* results_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 842AFED9641BA042831DB7383E12A2A0 /* results_notifier.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 1EEFBE2820F6BEAC9BAD184673293C9E /* NavigationAccessoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90123BC19B6698C2EA04510241E8A9F1 /* NavigationAccessoryView.swift */; }; - 1FB2164A2DD04DABA3E647DD8BA769A9 /* RLMRealmConfiguration.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 8441212AE3E623321A15CF9A2931A5E4 /* RLMRealmConfiguration.h */; }; - 1FFC323AD9671C953B21425F876E6A3F /* NSError+RLMSync.m in Sources */ = {isa = PBXBuildFile; fileRef = 0628BD4AB36BFDD0F70F5E1DB1DBF67A /* NSError+RLMSync.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 2189F5569BACCE66FD41F5E58FF351B8 /* DateInlineRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3646CE1E7000B6E5DACD713D0610F22 /* DateInlineRow.swift */; }; - 21A9DEA3E42240805DF7D849B250F009 /* RLMSyncSession.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 0981BEFA01FEAE538C1510758D563982 /* RLMSyncSession.h */; }; - 23E6887F5ADC8D4917BC12B079864E1D /* FieldRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17B7645C7D765D49975F13208959E78D /* FieldRow.swift */; }; - 243AEA4811C5A397D946CCDCA1BBC8F4 /* Pods-Anyway-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = DC6C3BEF7CF735F64C406CEE2FE2170E /* Pods-Anyway-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 25A70264F9301E7603D9883777DE8050 /* SelectorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29D0734057FB701FB6B90FC6A17E9025 /* SelectorViewController.swift */; }; - 25FAE49C26B45BE660E2BAAA1F3B8BB8 /* RLMObjectBase_Dynamic.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 32D27D06530CC7C698C09158E97B1714 /* RLMObjectBase_Dynamic.h */; }; - 26066AA5E214DDEA1F6CB59CEDB0CB6B /* ObjectSchema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86627A77089E844EBACD47C208071806 /* ObjectSchema.swift */; }; - 260A6263A4B8504601674415FB762CBE /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BEF6D640145744CB8BEB33726BCB7547 /* UIKit.framework */; }; - 265A3BCF8B426717E8EE1CAD4341F99A /* DateFieldRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03C6CE1F1C11DD39FEB2B8EF746CCC4E /* DateFieldRow.swift */; }; - 26F4CE107C3719EAF451E3CBE774EC6F /* RLMSyncConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 4527BBFB0222D834AEC2504878D0E941 /* RLMSyncConfiguration.h */; }; - 2799D136D7C0ECF8FDF37CBBA678E44E /* RLMRealmConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 8441212AE3E623321A15CF9A2931A5E4 /* RLMRealmConfiguration.h */; }; - 27A22C298CD655BDF0D016F9066CE5D4 /* RLMObjectStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 7216CA306EE64A30BA3F7624E146E93D /* RLMObjectStore.h */; }; - 28500C67AF0CB1EB34B53FFA93C3D27F /* ListCheckRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93C123B1B31F0D8126DFA0BD859D2379 /* ListCheckRow.swift */; }; - 29623846A0DB1602F62E22155449F52A /* RLMObjectSchema.mm in Sources */ = {isa = PBXBuildFile; fileRef = 467FB620E0DFEF99D2F491E6D28CD4E5 /* RLMObjectSchema.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 29DBA0C923DE13772022DB52DBAF3CEE /* PushRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5424BE0CC5F454C6457E5F589E917AEC /* PushRow.swift */; }; - 2A08A909E281D40EAF14DF5EE0A3055B /* RLMResults.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 18C1C43D04AF90570D89DF2922616B6E /* RLMResults.h */; }; - 2A179EA67ECDDBB23F30CF0B0A71A617 /* ActionSheetRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D403206900FBDE3E3E8F9E470DC1D7C /* ActionSheetRow.swift */; }; - 2AAAEA56F136003D17E38437CB6EDBE1 /* RLMRealm_Dynamic.h in Headers */ = {isa = PBXBuildFile; fileRef = 4138285A9AFDCD8EA8A861545BAED78F /* RLMRealm_Dynamic.h */; }; - 2BC8857FCF261B782B8198875DD8C7E9 /* sync_user.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B3C815EEAB1A46D4EB01E54A27B22C0A /* sync_user.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 2CB0F8860FDA3855A545607B7DB337C5 /* RLMArrayLinkView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 508004E59A84C9E2BD7545539AB2B89A /* RLMArrayLinkView.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 2DE065362AD086A43988AB0703DB9F48 /* RLMSyncPermissionChange.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BFBD5CE655281E8C717BADD52E4958A /* RLMSyncPermissionChange.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 2E72509E4A89E42ADA5925A9AA36730A /* RLMSyncSessionRefreshHandle.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3431B2BF741F061F001CE09129307498 /* RLMSyncSessionRefreshHandle.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 2EA139B45B1CC7CBB99924EF793CDDEF /* RLMRealm_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 60642B9E5F01ED218D76A28301764A6F /* RLMRealm_Private.h */; }; - 2F4DADD310C4D1D5A730DDFBEAF0B4A2 /* RLMArray_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E9E7653EE6E11A9D714A5FBD6F5698E /* RLMArray_Private.h */; }; - 2F73A899F8F8DA9FFA99D3BBC709D585 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A3D9DA2C71CA686A1C42E7E25069931 /* Foundation.framework */; }; - 306BD477167820D6CDECDCE1D7445ECD /* SliderRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50FB6F40AB6BB889316E02C3F71DFFB1 /* SliderRow.swift */; }; - 30B389B346A12D2E24E41F568891070F /* RLMSyncSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 0981BEFA01FEAE538C1510758D563982 /* RLMSyncSession.h */; }; - 351DB8BE8B6CA23BC78D5D54C8F5FB9D /* list.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2CC97A6737F1714F9EF84DDE43475C07 /* list.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 356E31AEDC2125D57154B92860274112 /* RLMSyncPermissionOfferResponse.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = D5ED347940A6390F828FAA476C837545 /* RLMSyncPermissionOfferResponse.h */; }; - 3626B94094672CB1C9DEA32B9F9502E1 /* TaskDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4A4FBE832D1BAFB8C77FA4082C6B113 /* TaskDelegate.swift */; }; - 362B59915C529779F5380F3F52C7A98E /* RLMObservation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A18C31291781314DD00F7C5EB52F56F /* RLMObservation.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 3720C7DF06CAC82BED96A2D65A3F2FC8 /* RLMProperty.mm in Sources */ = {isa = PBXBuildFile; fileRef = 43AB36B77AF38537BD827526EF2B7E97 /* RLMProperty.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 3A595525550DFDC17AA868B3367E0BD6 /* RLMProperty_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = A553DF821B37979C37BC0E3263C3F6A8 /* RLMProperty_Private.h */; }; - 3AC341F72F45E8CC0E585FE8C5ED7EE5 /* RLMSyncPermissionOffer_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 8E513C73F59953C62AFE3B41B410F108 /* RLMSyncPermissionOffer_Private.h */; }; - 3ADB85DDB88CA252FE72DED08FD3F749 /* RLMObjectBase.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 7EAA673441B602B9A00FF426FBD36DA0 /* RLMObjectBase.h */; }; - 3BFF9B9630C8E48FB861273FB4E00BC6 /* RLMSchema.h in Headers */ = {isa = PBXBuildFile; fileRef = C9F4EAB7ADB9F943D14DC790F2116D94 /* RLMSchema.h */; }; - 3D0274E408AD3EA2AFC308A983831163 /* transact_log_handler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DDA629004C61F9A1F688221E581BD35C /* transact_log_handler.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 3E2245F0E306A13226610E3841286673 /* RLMClassInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = C08AC3525CE6AE6E9A38F18D070EDF56 /* RLMClassInfo.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 3E89604392C5EBDC5E27937AA57C5AC0 /* SwitchRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAD4FBA1295E2A108DB8344445050359 /* SwitchRow.swift */; }; - 3EC1ED3912870E8CC346FD831738155A /* SwiftyJSON-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = A1C7E0F431B5E7F5D1489E545A37E07D /* SwiftyJSON-dummy.m */; }; - 3F0D960DBEF20736A7A505A9250942F1 /* Eureka-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 66460A48ED0E8EB446519BCC5A09057E /* Eureka-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 4048A09CFF693AE1A860FB7BB9079CDE /* RLMSyncCredentials.h in Headers */ = {isa = PBXBuildFile; fileRef = 6039B922F638860B9FC3874C915E0671 /* RLMSyncCredentials.h */; }; - 42749527759AEEA707A47239DE68B1B5 /* RLMSyncUtil.mm in Sources */ = {isa = PBXBuildFile; fileRef = 12D42F13EF40C8807F1B51A7E183C2A6 /* RLMSyncUtil.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 4439CC4507D923EF9BEC5F98FB2B432F /* RLMProperty.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 10BBAB9E19BCC36E9E4EBF8BBF70EDEF /* RLMProperty.h */; }; - 44C8BD81D49B9DAE791A880871661896 /* RLMConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = A7E633301E8BB0C26474AAA924E84975 /* RLMConstants.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 453B0BEA023B4C89CA3E3B110C72E9DD /* RLMProperty_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = A553DF821B37979C37BC0E3263C3F6A8 /* RLMProperty_Private.h */; }; - 465C53B75AD9146F11AD2BEC3698D268 /* RLMAuthResponseModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 11964F2393D2D4030E37AF622F8F7DF8 /* RLMAuthResponseModel.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 4664D8E7AA73A05367718FADC48028BA /* RLMArray.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 5783296C13B6AB8D723ED137E1D96506 /* RLMArray.h */; }; - 47284369D85B3F5D4E5AABC09C80F048 /* external_commit_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C7C80E15B5DE9055E1FB944495DC8BB /* external_commit_helper.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 49915E6633A6DD78BAC3F33AC716253B /* RLMObjectBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 7EAA673441B602B9A00FF426FBD36DA0 /* RLMObjectBase.h */; }; - 4B049F5FF6C31394D8C106443419BA82 /* RLMSchema.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = C9F4EAB7ADB9F943D14DC790F2116D94 /* RLMSchema.h */; }; - 4CD31C93C75FA5A1E98DACC14B3BD127 /* RLMRealm_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 60642B9E5F01ED218D76A28301764A6F /* RLMRealm_Private.h */; }; - 4D7C48608E43F3B056194EB8AA6213D5 /* RLMObjectBase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 270E75F85B71197FEACBC19FBE748F37 /* RLMObjectBase.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 4E26E1B18FD78C4599EA573A9679489F /* RLMListBase.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = A1FABEEC135187CA74F85B3F768148C3 /* RLMListBase.h */; }; - 4E82DE3BD3418A7E0F2BD2C9136B7334 /* RLMMigration_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 75DF55B3A46FC49075AE4BD6C9EA383E /* RLMMigration_Private.h */; }; - 4EA3738D7B4B9ED5E012BA7A9F2CC382 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A3D9DA2C71CA686A1C42E7E25069931 /* Foundation.framework */; }; - 4EA524A2BD2D970745B89DAADDF7719E /* PresenterRowType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A284B16D14F93475C41DAE1873649B5 /* PresenterRowType.swift */; }; - 4F40E7A85AA630F4FD3A6B091319E3ED /* schema.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5BEC800BBADAA10F12A2907AD52489C8 /* schema.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 51F62349D05FCB3B6E1E8AD406DEEFE7 /* Section.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03A6ADFB0A4503070DF6C5589F3F3E20 /* Section.swift */; }; - 526FEA939C967D5E183B06A931A0EAC0 /* RLMPlatform.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = D2A9F3F21B7819EF65B2FF6F110AF533 /* RLMPlatform.h */; }; - 52EF95446C4BAB921DC82EB006C739C0 /* sync_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D088C2DCE81B9C32CAFE3F16D7003F /* sync_manager.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 5387216E723A3C68E851CA15573CDD71 /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1A043D377AAD1E52073686C96FF4CB6 /* Request.swift */; }; - 53986046D32ECF5B83F5092E7273B01C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A3D9DA2C71CA686A1C42E7E25069931 /* Foundation.framework */; }; - 545176EB8BFA5C7897F7AA39BEC1C200 /* RLMOptionalBase.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = CE35665E296651539A94F942B81F95EE /* RLMOptionalBase.h */; }; - 54672FEBD3D00F547E71DA35A1ADE583 /* RLMListBase.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACEE2EB466686DFBBEF9A8D83DDF4BB7 /* RLMListBase.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 54F4FD105F0DC99B6EEB538162AEFD30 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = 240340D83B39E2F6B35CF6BC4CFD164C /* Property.swift */; }; - 568B4EC01165E670833A947131AC1596 /* RLMSchema_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = A9C3ECEAF6DD8DD56BC4C3B6BBF571B5 /* RLMSchema_Private.h */; }; - 57F029A6EAD66812E9B0C1319AFE527B /* LabelRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EFD0126BC7BBC68CDA40D17FF5FF701 /* LabelRow.swift */; }; - 58BCBF1A71B357291C76E826CC4D80C3 /* Aliases.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71878C452FE04FD80E0D2BE0842646A /* Aliases.swift */; }; - 59947C008333007F823B0922EE87F96B /* MultipleSelectorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36B2E6217FCAFD472E13DA1BFB06DD25 /* MultipleSelectorViewController.swift */; }; - 5A141D1AFC1301ADB773261E7B133090 /* SortDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E231FEB8719347E19C2093B5BB82875 /* SortDescriptor.swift */; }; - 5A380E0127635057397F45AFBD13B255 /* Eureka-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = F52E1A34E1E348A3265431E4106847D9 /* Eureka-dummy.m */; }; - 5B43EE354D8587C3925BC6D805772DBF /* sync_session.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CDBF32EDB2629D7DC97D7ED60FAFB32 /* sync_session.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 5B55B9BCDCEB99090615AACC36439906 /* Optional.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C82B5E5218853D5FF1D99A144639C0C /* Optional.swift */; }; - 5C4A031D35521F124639526D9CC59A18 /* SwiftyJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCFFE113430B44131E8A2FFE5C595B78 /* SwiftyJSON.swift */; }; - 5C4B188E302E48361E80ED6EA892C471 /* RealmConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89A08A820500A904B6233379A9817E7 /* RealmConfiguration.swift */; }; - 5D647533060C688C633E15C86553B868 /* DatePickerRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAD6BFE59B3D53D339C52EE758C9E1B6 /* DatePickerRow.swift */; }; - 5FE4EE3ECEE5FA37EBAA1A1F33927EFF /* Realm.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = E4CAF02FDE199C9936F3AAB9F5E80BA8 /* Realm.h */; }; - 600C400E08DD9EC2B3A70CA18919A3A4 /* RLMRealm.h in Headers */ = {isa = PBXBuildFile; fileRef = 1729FA2C3E2386CDE39B9777073DCDC3 /* RLMRealm.h */; }; - 61200D01A1855D7920CEF835C8BE00B0 /* DispatchQueue+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5064684CC6FE7B593161C2C62A7BDFB1 /* DispatchQueue+Alamofire.swift */; }; - 6298746B3A00D2EA171B3B964682FB49 /* RLMSyncConfiguration_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = CE12DBCCD12A2606CBE168BC27DAEEFF /* RLMSyncConfiguration_Private.h */; }; - 62F65AD8DC4F0F9610F4B8B4738EC094 /* ServerTrustPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B625B7E1AE15C871934DC51A58AB73E /* ServerTrustPolicy.swift */; }; - 63BF963098CCCDDC24BA51EC9604FC5C /* SwiftVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEFB9FD1287C8247D8948A9FBCB2846A /* SwiftVersion.swift */; }; - 64493EC4ED58A74CD0E2F8E9D7CAF8BD /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A3D9DA2C71CA686A1C42E7E25069931 /* Foundation.framework */; }; - 64983B09F0760BA0A1966B256A8F883B /* collection_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EB23711C1CEC6CF2337C5819D60BF159 /* collection_notifier.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 654744F1D8E5A8DB4711E81DC6CC99C3 /* AlertRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B0A8743C5F9B5685DF0FF5B8D162D8D /* AlertRow.swift */; }; - 690FCBD7E1A1D43E946316682D04B6B1 /* RLMListBase.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = A1FABEEC135187CA74F85B3F768148C3 /* RLMListBase.h */; }; - 6A994F95AC9DF62B10F0DDFD560F7BDF /* collection_change_builder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 13E8AC0A040A95EE5A90B6279F2FD01B /* collection_change_builder.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 6BEE4DE62BB8FF74379FD3214824ADB6 /* RLMPlatform.h in Headers */ = {isa = PBXBuildFile; fileRef = D2A9F3F21B7819EF65B2FF6F110AF533 /* RLMPlatform.h */; }; - 6C827723C97288F55395DBAA76BC5219 /* RLMMigration_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 75DF55B3A46FC49075AE4BD6C9EA383E /* RLMMigration_Private.h */; }; - 6DA0179B88EED766667D1171D035FF83 /* RLMCollection.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 57B66886E0B5A2FF762539B9888E7D31 /* RLMCollection.h */; }; - 6E032CEF899312F1D255ED1E5BA0EDF4 /* RLMUtil.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3849234A5CFEC796842D8A0FF2EEAAE4 /* RLMUtil.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 6E9EEF9D90454CBD675A126B6057B1FF /* RLMProperty.h in Headers */ = {isa = PBXBuildFile; fileRef = 10BBAB9E19BCC36E9E4EBF8BBF70EDEF /* RLMProperty.h */; }; - 6EC9027D918EF8ADE1ED87EC99FD2250 /* index_set.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 619BF1FD92B3D6FE97860B2CBF9229C2 /* index_set.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 6F9F25DE63532C8DE5077822E6316111 /* DateRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3D815140621A399552C48BF97759F23 /* DateRow.swift */; }; - 707A0E5F7DBC968D1340A1FF2B08BFD7 /* ThreadSafeReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DCBEA864CF823C46E898153B245D8E5 /* ThreadSafeReference.swift */; }; - 76110C1B9D1144D99D2C179DC74121DD /* MultipleSelectorRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9B2A16F6F51E0E5E465E8DF9B960BB6 /* MultipleSelectorRow.swift */; }; - 76336182ECAD0ACF61371D6F66D8F01E /* RLMSyncUser.h in Headers */ = {isa = PBXBuildFile; fileRef = 502995188A080E6D6436BF64DAC9E5B4 /* RLMSyncUser.h */; }; - 7A4C3B505A1F9899A1E61A8FC23F42C7 /* RLMObjectStore.mm in Sources */ = {isa = PBXBuildFile; fileRef = CB30E2CDA387263CFF9EA9C05C5218E3 /* RLMObjectStore.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 7B19019174C42D7D481C1B120272443B /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF277B8952F3E92239B0266AEFEB566D /* Operators.swift */; }; - 7B5FE28C7EA4122B0598738E54DBEBD8 /* SessionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7311A2BBD138BA02D3D1AFEB8009F42F /* SessionDelegate.swift */; }; - 7CE512D69EDD58D7DDD7B66BE6592EED /* DateInlineFieldRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6644A671463D040EAD5387939EB43DA6 /* DateInlineFieldRow.swift */; }; - 7D4103D396B2519600255CA676DE1D62 /* BaseRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B32E1E31A1D58627A01759618B4D79 /* BaseRow.swift */; }; - 7D6F4DBC7CFE7C6D11798E8F568A6F57 /* RLMObjectStore.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 7216CA306EE64A30BA3F7624E146E93D /* RLMObjectStore.h */; }; - 7D8CC01E8C9EFFF9F4D65406CDE0AB66 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 407F9684B0240170F95F9A6F7E844E68 /* Result.swift */; }; - 7DFD7F9215245EA744F91515EF68096A /* RLMSyncPermissionChange_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 2E3B856EDE75C56D20E19B60DA7C900D /* RLMSyncPermissionChange_Private.h */; }; - 7EA353CB5698899BF205F84F3880969A /* RLMRealmConfiguration_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 24CA718B9D06B3DE1F62BB0C374BE311 /* RLMRealmConfiguration_Private.h */; }; - 7EDEC38A5EAD3F44F78F3399A5B0EBD4 /* RLMSchema_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = A9C3ECEAF6DD8DD56BC4C3B6BBF571B5 /* RLMSchema_Private.h */; }; - 800858C78D1B635841E57207CED72E25 /* collection_notifications.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9E4FF1360A4607377FBB5B1B3AFEEA7A /* collection_notifications.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 81F432D75037041DE0DC1CE348DA8B1D /* RLMSyncUser.mm in Sources */ = {isa = PBXBuildFile; fileRef = BF3CF670586246BAEC381F6EB258D7C1 /* RLMSyncUser.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 824069A0AD3321FE296EF88D544CB3D9 /* RLMThreadSafeReference.h in Headers */ = {isa = PBXBuildFile; fileRef = 38AA4F2C6E7EAC364B55224D98763F0B /* RLMThreadSafeReference.h */; }; - 827BD4C80F28C2F7E9521461DA5CBDAE /* RuleURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D9BCF83EC8706B0D38C3F5CA6912FF3 /* RuleURL.swift */; }; - 847DFA085558797301022D6F2A769CD0 /* RLMListBase.h in Headers */ = {isa = PBXBuildFile; fileRef = A1FABEEC135187CA74F85B3F768148C3 /* RLMListBase.h */; }; - 892D05EB11574FB6E74F81CF2482D97F /* RLMSyncUtil_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 6C05CDAFA31C3E20FFA2BA6425E1CEBF /* RLMSyncUtil_Private.h */; }; - 8AD89A0DA3173352A5D18C5DF3AA333C /* placeholder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 393D848359E7D9EA8202A481BE76DB05 /* placeholder.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 8BBE96797C2C3A52A6132A2FD8B4527B /* RLMAccessor.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 27C14377DF2A3FBB3817B4C9B050AB7F /* RLMAccessor.h */; }; - 90FDEA1F7BED538FC24102EF109987D6 /* RLMMigration.mm in Sources */ = {isa = PBXBuildFile; fileRef = D4CDCDC351D38138ECCED267801C5CBF /* RLMMigration.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 923E5D2F577BB94EEB395EA4D4F322AC /* RLMSyncPermissionOfferResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = D5ED347940A6390F828FAA476C837545 /* RLMSyncPermissionOfferResponse.h */; }; - 94A050F1B8274A83A743CE3984A988D9 /* RLMArray_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 0E9E7653EE6E11A9D714A5FBD6F5698E /* RLMArray_Private.h */; }; - 94C170F089E28582865BBE1C36319F3A /* RLMRealmConfiguration+Sync.mm in Sources */ = {isa = PBXBuildFile; fileRef = 882F164A5D289B372807FDDBD1A1CB62 /* RLMRealmConfiguration+Sync.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 9779299ABD0BEA143A63517A8847E2A5 /* RealmCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = E665E84C8E4BB0E3803FB153ADF872E7 /* RealmCollection.swift */; }; - 987A9AA62B48BC871C21106E68F9257E /* RuleEmail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 978B783E5ACC1ACAE6353A5AB032D93F /* RuleEmail.swift */; }; - 9926EF73DC7EFCBD50D322671328B24F /* Cell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 084D1115E70849A725D618FF67761945 /* Cell.swift */; }; - 995EC88763825C70CC694BCA7EEDFE1A /* object_store.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D824F4C8D4EFE3AEE8781A238BF06950 /* object_store.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 9987E972D1CEE4FD2F35D70310341EE9 /* RLMSyncPermissionOffer.m in Sources */ = {isa = PBXBuildFile; fileRef = 92549DA3A81A89CD2DCD9EDF70CE9D85 /* RLMSyncPermissionOffer.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 99BC63A28EB55DCDC2CA8EAC1886E077 /* StepperRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8991107A2B7B547BA854EAB256F08781 /* StepperRow.swift */; }; - 9A1C100D9F5A49B864F6CC42C5894900 /* RLMResults.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2295D12C85920E4B1E3F7706468DD794 /* RLMResults.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - 9C3A69B8128A844F6EF3A158E7F07986 /* NSError+RLMSync.h in Headers */ = {isa = PBXBuildFile; fileRef = 8241C2625DF4891B7FC2A4445198A6CB /* NSError+RLMSync.h */; }; - 9C84133A55BEF2AB3E00326F2F93BB62 /* Sync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0039B9DDC2472BE35918784A90AA0B /* Sync.swift */; }; - 9CD46DF12CFA5C561B75EF20B95EECD0 /* PickerInputRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF96E6C5E79D7F0FFA45A108EE7E3CB6 /* PickerInputRow.swift */; }; - 9DD39D0018BD08D5A72089ADF663F85F /* SwiftyJSON-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 8FC1C64BD2D33913F8DDD197764A345E /* SwiftyJSON-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9E32655D8CA41C0FD3B1BE4829D11EF3 /* RLMSyncUtil.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = FEA2ED6C6EA03296AF7255E16C77C90B /* RLMSyncUtil.h */; }; - 9ED2BB2981896E0A39EFA365503F58CE /* AFError.swift in Sources */ = {isa = PBXBuildFile; fileRef = E06D431FD333C8EA06E4F2DF70DA7D76 /* AFError.swift */; }; - A00D26EC4C0564550CFD698E63D66C53 /* LinkingObjects.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA2FDCFC499C20ED3E774D1316DC884E /* LinkingObjects.swift */; }; - A04BFC558D69E7DBB68023C80A9CFE4E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A3D9DA2C71CA686A1C42E7E25069931 /* Foundation.framework */; }; - A0E6C2F2A364C6349BD2B79F520A8958 /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 773791FAABE10D0CD841A405B8EBD833 /* Helpers.swift */; }; - A290DFCED7C29574C0153583BF6C4BA3 /* RLMSyncManager_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 505EB98642A13AD8D2015D0E58D9181F /* RLMSyncManager_Private.h */; }; - A295E57EFDBBDBA3A4D79815A7FA35DE /* Realm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0325E7C7F5AC1AE712D7975B1699FFE5 /* Realm.swift */; }; - A2A6F71B727312BD45CC7A4AAD7B0AB7 /* NetworkReachabilityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FEC3405757873883235969FC527F2CE /* NetworkReachabilityManager.swift */; }; - A380C8BB57E05BA91CD7B44756107E55 /* NSError+RLMSync.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 8241C2625DF4891B7FC2A4445198A6CB /* NSError+RLMSync.h */; }; - A38A545D1ADAFAB39F6A28B499D9AAB9 /* GenericMultipleSelectorRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC0C3EE90D3935E7766DDB5EF134852F /* GenericMultipleSelectorRow.swift */; }; - A429504524879F6BA5F4A3DB8B5DAB09 /* shared_realm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1D4BCE97A71DE1CDE56EEF209B6FE7CF /* shared_realm.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - A43E0EFA21B9CE76E7103E8A8C7EFBA3 /* RLMSyncManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 834DAC5B97D31C4295577526C26859DD /* RLMSyncManager.h */; }; - A5B27A12053041F38D47538E48678881 /* Validation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA6E5A8C807057AA6361F8C7A5700BFE /* Validation.swift */; }; - A9EEEA7477981DEEBC72432DE9990A4B /* Alamofire-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 5984CBF88D06136A0A23DE62752A4CB7 /* Alamofire-dummy.m */; }; - AA0CDD726F8156D06FDFE3C6FAB27C52 /* ButtonRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC3368D01A978261649708EACBBC2E61 /* ButtonRow.swift */; }; - AA7012D4A2E0818B5D6C2BAA5DFA3CD2 /* RuleEqualsToRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373AB91A812DCAD2C3036DA8E3DEDFB6 /* RuleEqualsToRow.swift */; }; - AD76C9D1DA235A363D2606A874E565F4 /* Pods-Anyway-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = F5203DAF180A9A8801705F411B711B01 /* Pods-Anyway-dummy.m */; }; - ADCCBB44A2FDC9610AF79DD03F9812BB /* RLMNetworkClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F550759D3307B38E90A1D9799D000DF /* RLMNetworkClient.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - AE1EF48399533730D0066E04B22CA2D6 /* SessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8DC206D5ABFFD9DCC07EA5B9A28D151 /* SessionManager.swift */; }; - AE632CEA88677A7B6D1C5F5932986266 /* Realm-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = B46433D888982EFBB459A99558D4D810 /* Realm-dummy.m */; }; - AFD0AD548634699D7D1C318038122D52 /* RLMObjectBase_Dynamic.h in Headers */ = {isa = PBXBuildFile; fileRef = 32D27D06530CC7C698C09158E97B1714 /* RLMObjectBase_Dynamic.h */; }; - B072733E59B22561B4C1038185F0D688 /* RLMSyncConfiguration.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7FEF75D28C51DD2C5CB7D482BBC916EA /* RLMSyncConfiguration.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - B1DBE4FCD5C52290C5977E2B00554FEB /* RLMMigration.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F163D3E628B2C27B1508E055FCBB4FE /* RLMMigration.h */; }; - B24E96BE6BDBD2217A6F798A1DF2A134 /* RLMObject.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = C0728F2E5AE5F61D049A15932F78BA19 /* RLMObject.h */; }; - B3B5B10201D9BA6470173163A6F6725E /* SelectorAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB147AC31F137C05E67DC35CBB4EED2E /* SelectorAlertController.swift */; }; - B3D3B1C729C78880483502AC7C662E0E /* results.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1E5E1F7504471A7DC6A681EC7E71A87D /* results.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - B5BA32808AFF5DC9C2723CD85931B9DB /* TextAreaRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8225E11B668E56A71D602CB391CBF5C /* TextAreaRow.swift */; }; - B65FCF589DA398C3EFE0128064E510EC /* MultipartFormData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 386DCD377B9E54E299E4F8CEC8FF48E3 /* MultipartFormData.swift */; }; - B68A1586D3548E912AC1D46EAD369611 /* RLMSyncConfiguration_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = CE12DBCCD12A2606CBE168BC27DAEEFF /* RLMSyncConfiguration_Private.h */; }; - BA94E6BD0A3489E15B5ACE43E8FE67C7 /* RLMPredicateUtil.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84471FEA32F71EB36161B5F2954E43B0 /* RLMPredicateUtil.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - BB04DAD5C6DCAE93C15FED1090520610 /* RuleRequired.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA29F62615D2D85B1E418BED094A73C /* RuleRequired.swift */; }; - BB1BD27373ECC97C862538EEA7E18643 /* RLMThreadSafeReference.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9E5E772C1B03083A40994D3FD70D5426 /* RLMThreadSafeReference.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - BB8B734691BFC0B1E2CF10CD4CA5857E /* realm_coordinator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 15DB1C10C2C45C61470E5FDAD410FADF /* realm_coordinator.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - BBEFE2F9CEB73DC7BD97FFA66A0D9D4F /* Validation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 686F5B1BAF09AE338CA46A328DE359FB /* Validation.swift */; }; - BD5F09163374DDDEC7F990F6E32C57C8 /* RLMSyncSession.mm in Sources */ = {isa = PBXBuildFile; fileRef = 199A99A47685911D68DE2E3BFB8468EC /* RLMSyncSession.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - BD6FEBF93A8FB23057B38BC52342AF19 /* Eureka.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 1E13F8D90E94321D9AC567411EEAFEAB /* Eureka.bundle */; }; - BE5C67A07E289FE1F9BE27335B159997 /* ParameterEncoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DD011B908DA95F09D9D14D38BA0249 /* ParameterEncoding.swift */; }; - BE8036F2B830A87E24FA93058208813C /* List.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956AEA5CF04A5AB3EC1142C5720085C2 /* List.swift */; }; - BFE53EDC99E6B10F19E85ECBD01FB33E /* RowType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 520F99B6CFE5ED981E6BAC5559A35473 /* RowType.swift */; }; - C2631DEBEF1988EFB007B6C15D526835 /* RLMRealmConfiguration+Sync.h in Headers */ = {isa = PBXBuildFile; fileRef = 11A77345025CB3D7B627A351D33D7F5A /* RLMRealmConfiguration+Sync.h */; }; - C2FACC33902E82500AB1584B05079027 /* RealmSwift-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = B5AB4446CFFCD1BF3787BBC4D68654CB /* RealmSwift-dummy.m */; }; - C30D239388B285B78BB4ED6E99FC2751 /* RLMObject_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 08FA7D650AA736A88B61D2537CB3CD51 /* RLMObject_Private.h */; }; - C319C0C9044EB123E85259CD381E6321 /* RLMOptionalBase.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = CE35665E296651539A94F942B81F95EE /* RLMOptionalBase.h */; }; - C4CA79DE19A03ADC323D8B384A81FB4A /* RLMObject.h in Headers */ = {isa = PBXBuildFile; fileRef = C0728F2E5AE5F61D049A15932F78BA19 /* RLMObject.h */; }; - C657B4C0DFB6C3BE1119C1BB8CAD4C78 /* RuleLength.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD09069DAF0A3339E871F4D51BE6BB73 /* RuleLength.swift */; }; - C70833175BDF0C7B69DAC6BC96C9B017 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A3D9DA2C71CA686A1C42E7E25069931 /* Foundation.framework */; }; - C7393325061C6DF2959B36FD0D581FF3 /* RLMRealm.mm in Sources */ = {isa = PBXBuildFile; fileRef = BE87BD78CE2474F4AA9F7DDF39CC8A79 /* RLMRealm.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - C7C9DEA03660DCD66A101DCADB308C96 /* RLMCollection.mm in Sources */ = {isa = PBXBuildFile; fileRef = EF964A1E8476F05A9A9F4A4D6259A1DD /* RLMCollection.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - C871C012ECCD48DB4C926F8A890C2474 /* RLMSyncCredentials.m in Sources */ = {isa = PBXBuildFile; fileRef = F90EDF64FEF32A1E26029BB85ED1C56E /* RLMSyncCredentials.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - C8DFFCF5E1FD9DB1C01640461EFC1460 /* RLMObject_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 08FA7D650AA736A88B61D2537CB3CD51 /* RLMObject_Private.h */; }; - C905D1D56E63424E065733EEFDF885B4 /* RLMSyncPermissionChange_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 2E3B856EDE75C56D20E19B60DA7C900D /* RLMSyncPermissionChange_Private.h */; }; - CA5A604A65CCD04D66E35737677056DE /* RLMObjectSchema.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = FDBD80532A14DC83B71C9D9D1B548103 /* RLMObjectSchema.h */; }; - CB1802C7C49FC703CA11AA3F807E2A49 /* RLMObject.mm in Sources */ = {isa = PBXBuildFile; fileRef = A2E62568C016C9FD6DA9E44A207C7B9C /* RLMObject.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - CB6D60925223897FFA2662667DF83E8A /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6952E45E49E7F662E8E8F9B51CA18863 /* Response.swift */; }; - CB71100265DF9B80D65E60BBB9670353 /* RLMThreadSafeReference.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 38AA4F2C6E7EAC364B55224D98763F0B /* RLMThreadSafeReference.h */; }; - CB7893F1F8C12A415F474AAEA7360CA7 /* Realm.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8D4A407D80D0F5AD849A44674551336F /* Realm.framework */; }; - CC608E72A4E1D0E7F7FB9CB971FCF084 /* RLMSyncPermissionOffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 9905EB2DBE88423775498C0762231D31 /* RLMSyncPermissionOffer.h */; }; - CCAB89C5B0F52B0F69DBED084DDFCFBB /* OptionsRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D127167FD3356138E35CD696019AFDAC /* OptionsRow.swift */; }; - CD2F4121E14584F475298E058862BAE5 /* Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = D19A6659DD24000C3DCCE171456D16F2 /* Schema.swift */; }; - CD9DEB0D28DFDC0ED1496CEDCDCE45FC /* RLMSyncUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = FEA2ED6C6EA03296AF7255E16C77C90B /* RLMSyncUtil.h */; }; - CEFF4977CABA1FF9E1629DBD857EC644 /* RLMRealmUtil.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA25F698977EFDD9705EDE8D7263E3B1 /* RLMRealmUtil.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - CF1E8CE44F4C4104A21C7F7EA06F030F /* RLMObjectSchema_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = EB338157B00F36FB6060E70B536BDF66 /* RLMObjectSchema_Private.h */; }; - D3970B0F6D5C4B87D8BF8D82B5730885 /* RLMRealm_Dynamic.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 4138285A9AFDCD8EA8A861545BAED78F /* RLMRealm_Dynamic.h */; }; - D64EEA42DA4A15A9CA19AB445465E0E0 /* RLMResults_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4864FA14696A7225D14E5BAC8F7F448D /* RLMResults_Private.h */; }; - D68A349751A7850D73EF9D1CA1452C52 /* RLMOptionalBase.h in Headers */ = {isa = PBXBuildFile; fileRef = CE35665E296651539A94F942B81F95EE /* RLMOptionalBase.h */; }; - D7A4E12989499842EBB17D60A32E663E /* ObjectiveCSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D3821CDA73B90CD3BD9B05955E5BC4C /* ObjectiveCSupport.swift */; }; - D7B1B0A497CE8975B5B8187FBFF00798 /* RLMSyncPermissionChange.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = DD4813B305768312DFCC46E17B165E9D /* RLMSyncPermissionChange.h */; }; - D8EE76C28F804327C31A2A319544E619 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = B07454B0F2C1553B654398EC391025D6 /* Error.swift */; }; - D9284875BF00A2033E41A8252CE907A5 /* SelectableSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC6BDBD16312E5555C4A809908EE5F11 /* SelectableSection.swift */; }; - DAB3C9C0EB19A56A34ABD018DBBD301A /* RLMRealmConfiguration+Sync.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 11A77345025CB3D7B627A351D33D7F5A /* RLMRealmConfiguration+Sync.h */; }; - DB5214501DA0045F69B3F336F0AB11E8 /* SelectorRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 449CF09F2B27B4F97F08D2453AAC4DA6 /* SelectorRow.swift */; }; - DBA264E204F15C44AC678A611A2D60EC /* RLMTokenModels.m in Sources */ = {isa = PBXBuildFile; fileRef = F633BCB79FCBA0377A53C7E63ED27D02 /* RLMTokenModels.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - DE6DF70A452BF20CB858BA7072AAE3EA /* RowProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C59F0E01E7F300BE1CFDDECCB8180EF /* RowProtocols.swift */; }; - E0946CE4B110F2A64041FAB63546FB48 /* RuleRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53FB91EC150CA439DF95B39FE6543C6 /* RuleRange.swift */; }; - E1A9ED5C87F358716FADD70967790B46 /* RLMAccessor.h in Headers */ = {isa = PBXBuildFile; fileRef = 27C14377DF2A3FBB3817B4C9B050AB7F /* RLMAccessor.h */; }; - E1DD9D55EE4D1BFFB4F09DAE9EED98E5 /* sync_metadata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9F59515DA257EEB12738DD612E21D316 /* sync_metadata.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - E29DB2B065666CA02FD4BC57968C4A71 /* keychain_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D3B238037C8AC4D64934FAEBE88C61DA /* keychain_helper.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - E31295691D8A8114E312311EF403BDFD /* list_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00BD4125040D26E6F494F4A6BD624460 /* list_notifier.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - E31AFF108FBD023D6CB081E257034CD8 /* RLMCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = 57B66886E0B5A2FF762539B9888E7D31 /* RLMCollection.h */; }; - E3345ABB043E3BDC4612991B57FB3253 /* RLMRealm.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 1729FA2C3E2386CDE39B9777073DCDC3 /* RLMRealm.h */; }; - E3B9AB2FF84700F819C0915DC920147C /* RLMAnalytics.mm in Sources */ = {isa = PBXBuildFile; fileRef = 686701257DF37DBC70A18137BAD60B7D /* RLMAnalytics.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - E490A481C69CE189A0814626B1251FB4 /* RuleClosure.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FA9915B3202603A2CEE9969409231EC /* RuleClosure.swift */; }; - E5810E41BDE5EFBA35111598FC81A410 /* RLMSyncPermissionOfferResponse_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 0674E972D05DC625A513B8892778003F /* RLMSyncPermissionOfferResponse_Private.h */; }; - E585A7898FC181529FD2704BEF4CE992 /* DecimalFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C3C9AC34A7A7C83578E7E6D2B8F0101 /* DecimalFormatter.swift */; }; - E92B5113A74D07D38C8137460AE0E3C7 /* RuleRegExp.swift in Sources */ = {isa = PBXBuildFile; fileRef = F84F232C37EF8A2DD0C9FF8DD9ADFC08 /* RuleRegExp.swift */; }; - EA7673C8C824BECA5901AACDB3A9BAC7 /* Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CED07EB9BDAFA7879F0A30A5824B79D /* Util.swift */; }; - ECF00D7E9037203EC2BC5110B60F4B10 /* RLMSyncManager.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 834DAC5B97D31C4295577526C26859DD /* RLMSyncManager.h */; }; - EDC407467AD35E26462E2E595C7A9AC0 /* format.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FFBCFC6554676810CEBD0FB23414331 /* format.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - EE7A2300183F8B7DD9A162D133B8032A /* RLMSyncCredentials.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 6039B922F638860B9FC3874C915E0671 /* RLMSyncCredentials.h */; }; - EF5C1615F952354B5A9F02E5F1D4ED39 /* Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 920C51B4A494C88164B00C22CD447B4B /* Migration.swift */; }; - EFC0F73130B1BB595C497FAAD41DC21C /* RLMConstants.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = AC10336CC1E93B42D3F920ED569F5259 /* RLMConstants.h */; }; - EFD264FC408EBF3BA2528E70B08DDD94 /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7EA768DC3032A622F5542785D38139F /* Notifications.swift */; }; - F09AA1B0410CA9380A78556A618FCBA1 /* RLMSyncManager_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 505EB98642A13AD8D2015D0E58D9181F /* RLMSyncManager_Private.h */; }; - F0ED8E0D78E1B7374EFD4EC66EE9FAD2 /* RLMResults_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 4864FA14696A7225D14E5BAC8F7F448D /* RLMResults_Private.h */; }; - F1681F8D16AD91A13187576F524783A8 /* HeaderFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7BC9AA4FF20F481CD6316CF967E7E44 /* HeaderFooterView.swift */; }; - F20893A324995F692FB8C2EBA32AAE8F /* RLMSyncConfiguration.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 4527BBFB0222D834AEC2504878D0E941 /* RLMSyncConfiguration.h */; }; - F2095372A5BB341F1B5469F634EA6687 /* FieldsRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04F8D2F7C2B72C1FE0F3F9CFCD347B9C /* FieldsRow.swift */; }; - F235CE79B80EA08ADD4B422B5E07E287 /* RLMSyncManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = EEEAC4F001764E87237EFCC8BC4A705C /* RLMSyncManager.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - F2B28126157940CAADB85D64B32795AE /* RLMOptionalBase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 11BE0F9CF79C0D969D2CDF3F990A34DA /* RLMOptionalBase.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - F360A8B4754FDA4EFE775E6D4547962D /* RLMArray.mm in Sources */ = {isa = PBXBuildFile; fileRef = 846820F180FF593D8B30F33F453BF0B8 /* RLMArray.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - F6BECD98B97CBFEBE2C96F0E9E72A6C0 /* ResponseSerialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1FA3F092353477F953E1F680C029226 /* ResponseSerialization.swift */; }; - F6C43E1F6A098750EE456A04E46565DA /* RealmSwift-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 0BD5E7FE8D39F82606529218C8B9EE24 /* RealmSwift-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - F6FBA9E3B0B2AD6527F8699B6352EE5F /* RLMSyncPermissionOffer_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 8E513C73F59953C62AFE3B41B410F108 /* RLMSyncPermissionOffer_Private.h */; }; - F721D2C02D5FE979E7638DC5FFB5FAA8 /* object_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 894692C9AB9605E81E9C6D693B2CDEF7 /* object_notifier.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - F74F2CAF27CBB42118FED9DEE3604EBB /* object.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5C41146D58D9108139484E4619940C2E /* object.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - F7D14B1756A81FB8BDD58A4B2BEF110A /* thread_safe_reference.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CFFD4E41A1EF23F20AAD44BC3F359A6A /* thread_safe_reference.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - F8B3D3092ED0417E8CDF32033F6122F5 /* Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83BDBC64DA966D95C5E7985BF558A63 /* Alamofire.swift */; }; - F9BA17EDA26513DA8F7BCAD3EC9DE885 /* RLMSyncPermissionOfferResponse_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 0674E972D05DC625A513B8892778003F /* RLMSyncPermissionOfferResponse_Private.h */; }; - F9F10B7DAEE14E60B2F7C261C389DE63 /* RLMObjectSchema_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = EB338157B00F36FB6060E70B536BDF66 /* RLMObjectSchema_Private.h */; }; - FBE464DA0DD491988B47B8B9E3FBF4BD /* RLMSchema.mm in Sources */ = {isa = PBXBuildFile; fileRef = 62F10F059C31B04CCD3DBB715790FADC /* RLMSchema.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"2.4.3\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; - FC505268B5A20F0F00D3BC0A9FF66F99 /* SelectableRowType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9659455EF6CAF526AD0F5AEEC11A5CB7 /* SelectableRowType.swift */; }; - FC7EFA7C36B6FA31AD54ED04AAEB280D /* RLMMigration.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 0F163D3E628B2C27B1508E055FCBB4FE /* RLMMigration.h */; }; - FD579EB9BB47796E30111D838FF01277 /* Realm.h in Headers */ = {isa = PBXBuildFile; fileRef = E4CAF02FDE199C9936F3AAB9F5E80BA8 /* Realm.h */; }; - FF0168C08BB3555924897006B4AD36DA /* InlineRowType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531746272E0DBC0075555120A675147D /* InlineRowType.swift */; }; - FF706EFA096ABE46C4BE087E54BABCDF /* RLMObjectSchema.h in Headers */ = {isa = PBXBuildFile; fileRef = FDBD80532A14DC83B71C9D9D1B548103 /* RLMObjectSchema.h */; }; - FFBC51DD335C01104A67BDC34483EC8D /* RLMArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 5783296C13B6AB8D723ED137E1D96506 /* RLMArray.h */; }; + 012D2917EAF8A5066C7D301EF17159B6 /* ButtonRowWithPresent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC703D03A42D8D2D25BCE9D7CE1DC68D /* ButtonRowWithPresent.swift */; }; + 024E96F046BEA94D3002FB34D0B684AC /* RLMSyncManager_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 1B57A881AACF13C810FE8799270BC239 /* RLMSyncManager_Private.h */; }; + 03F67C413386BDBE1556F0A672EE9DFA /* SelectorAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F6043E48DDC0ABA3B90D64A62B848BB /* SelectorAlertController.swift */; }; + 03F7C84AC2C8A9ED1DB5142A397702CF /* Validation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A7F0A0B8DFBE77352F497BE3B698066 /* Validation.swift */; }; + 04E4625EB2A26D8ED629FADF9E43843F /* RLMThreadSafeReference.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 7A773B69EBB1239A6A18F2BB26781989 /* RLMThreadSafeReference.h */; }; + 0572E357DE1A281CDA359D2C9A0FCD58 /* LabelRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FD0A44DB5FB29D4B787E94698D3504A /* LabelRow.swift */; }; + 05FA77B0DE799837578ABC6D82C06549 /* RLMOptionalBase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8FEEFF42B6B8348C08E9D90871789F14 /* RLMOptionalBase.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 09A91871E469450301A2A804D0EBA656 /* RLMRealm_Dynamic.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 8EF03B0A1821B57A4D813FC44E6DA4DB /* RLMRealm_Dynamic.h */; }; + 0A2BA5C92C965AD75DE3421529F36FEE /* RLMRealmConfiguration+Sync.mm in Sources */ = {isa = PBXBuildFile; fileRef = C98E314F648A32E9F16CCEF7FB450E16 /* RLMRealmConfiguration+Sync.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 0B101BA9AF54D6628C9988EB0C11DDD4 /* results.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 40586DDC9DF702A45F5EBEC431419383 /* results.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 0B8EB6B80F7C87AEE216BF9BB474CC0C /* collection_notifications.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1B0270EC4A80A78F37460EDA7A9EB51E /* collection_notifications.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 0BBEC18C19AD617FB56516D3CDEC6082 /* DateInlineRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4BE8DAE4F5784F3878092A687D0F7FE /* DateInlineRow.swift */; }; + 0C842CF4F10ACA094DC9A3AA79CE9D79 /* RLMSyncSubscription.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83A0A28CC02CD6AB04BB51F73F8CB169 /* RLMSyncSubscription.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 0D50E31A4C7A910EBBC0DADB0FA6971A /* TriplePickerInputRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9BFF632E3DD6218BC5959537D0763A /* TriplePickerInputRow.swift */; }; + 0DE5E6F6D5A8C3F356F0087B7BA16191 /* RLMObjectBase.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 6DC7EB8E3DE789DDA96E80E38F29D51B /* RLMObjectBase.h */; }; + 0E0A9D3F3990B117061FC5D459204484 /* RLMSyncManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 03330053F888BD923BF4C4CD4C46800C /* RLMSyncManager.h */; }; + 0E489B667253F643A20163185C0684E3 /* RuleClosure.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE22EFE05EAF99DD2057C42C33758987 /* RuleClosure.swift */; }; + 10EB23E9ECC4B33E16933BB1EA560B6A /* Timeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AB6D864DBC97BBB5C7C05F5540E404B /* Timeline.swift */; }; + 113E540A48C9788488C829D064F0426C /* sync_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE8FD25136279F7A06411526D1F4CEEF /* sync_manager.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 11AC774E046526553C87A3E70BFD02AF /* RLMMigration_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = BFC0EB7C7CE270DD215E17D9351C311A /* RLMMigration_Private.h */; }; + 12144081E1F96FE5DDC9226FC87914B4 /* RLMRealm_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 28210C7A86CBE8162C3D55C22079D4B2 /* RLMRealm_Private.h */; }; + 12879CBF252177764BE31ADA5A5A3BD1 /* RLMMigration_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = BFC0EB7C7CE270DD215E17D9351C311A /* RLMMigration_Private.h */; }; + 14815D0A1D15AE55C46865E21BEFB577 /* RLMPlatform.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A8920FC9084B4C9A81116734A9749A0 /* RLMPlatform.h */; }; + 15103A8FD65EA7CAE8BBF5B3B0EF9905 /* Results.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8438FFD5AE38CCAE72808A74F062ECC8 /* Results.swift */; }; + 15C90A014A579622A2C7C760611176AB /* RLMSyncSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 46420844EAA978DC4528A13BB970D63B /* RLMSyncSession.h */; }; + 16C520E3A004519E6DBBF91DCA0BD24B /* RLMRealm+Sync.h in Headers */ = {isa = PBXBuildFile; fileRef = F42A8422312B929411F5B93DEEEFA7F8 /* RLMRealm+Sync.h */; }; + 17229DA3C3EFB4BF4A11DCAA21500DCF /* RLMThreadSafeReference.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A773B69EBB1239A6A18F2BB26781989 /* RLMThreadSafeReference.h */; }; + 17E224A6EC632ECE28E85F5634F6BA7F /* RLMJSONModels.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F4BF5CD03006FF03F3A27B25DC0F87C /* RLMJSONModels.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 18DFEBB2A5BCF28AF31909F0D1BE38F3 /* RLMSyncCredentials.m in Sources */ = {isa = PBXBuildFile; fileRef = 590D565903C5870CE8E04F9714DCADAF /* RLMSyncCredentials.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 194D147D6A3C15FFB99900A925FC8B9A /* RLMObject.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = B3886B0BB14B2EF5C4262922ECF3DE2E /* RLMObject.h */; }; + 19AFC8077796C94D7DD31B0E7F50DB03 /* Eureka-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 0488E4C9B99E1C7153BD52CF62BE6FF5 /* Eureka-dummy.m */; }; + 1ADD9667E804CB33EED9B0C2F25FCDB2 /* Object.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28F0C763A6216AC9BEF1488A3FF128B6 /* Object.swift */; }; + 1B9EDEDC964E6B08F78920B4F4B9DB84 /* Alamofire-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = AA54227EA9DEF8A1F486F1ECE61992F0 /* Alamofire-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1C93D86DA0AEB40028BABC3A3DE73B7F /* Row.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DE5C10D442C8A56503D53FC9EFE1384 /* Row.swift */; }; + 1CA80508B65F46016A58E5A8B69366BC /* RLMAnalytics.mm in Sources */ = {isa = PBXBuildFile; fileRef = 04002741C9DC9598E4522E5C7784BE74 /* RLMAnalytics.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 1FF47336119005CEC78DA4DA843F2FB8 /* object_store.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DBB517B3130AECB9E6AC13567F48BC38 /* object_store.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 2035148E28F106A8F2E40C5E49DEDAFC /* RLMRealm+Sync.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BFE492D70CF60F74A461A4C34BA70F0 /* RLMRealm+Sync.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 22F79E28426BDBB51323B498D5DD9D97 /* RLMCollection_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D710B0CC80C2FDA501FD4F5D2FC52B2D /* RLMCollection_Private.h */; }; + 23C4D0AB263D248C4468D7050DFC78E0 /* RLMRealmConfiguration.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 558D13F22302404CE3C9B913877AE37A /* RLMRealmConfiguration.h */; }; + 243AEA4811C5A397D946CCDCA1BBC8F4 /* Pods-Anyway-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = C84FD9C200D57EE2834B9EF29FA80553 /* Pods-Anyway-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 24412725F72AB781C65FED663E5B25DE /* Eureka-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = D36EF80003D71394190566A94C169AA0 /* Eureka-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 24847FB79D2CF8030E94D1EB8F084B0C /* RLMArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 477D374261C345967081120A29C9D12C /* RLMArray.h */; }; + 25D8D0458BCA197BAC764DFC2CBFD289 /* RowControllerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CEEBEA40DD326F9EE6940AD39294427 /* RowControllerType.swift */; }; + 25FB7C3D82CA6C541F1CE8BA892D1DD3 /* NSError+RLMSync.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = B1DC356638CEC2013EB2AA5C48324795 /* NSError+RLMSync.h */; }; + 26066AA5E214DDEA1F6CB59CEDB0CB6B /* ObjectSchema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8B8BB4B7023504359EB1746965A799 /* ObjectSchema.swift */; }; + 2634B2AB72D5D8ED489591213E014936 /* RuleRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E6B269162560363B382DF5BC7EF1C10 /* RuleRange.swift */; }; + 26565FD8C9960FF8904D2CAAB2C6BB4B /* HeaderFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B5212E251572BA3D8FEF34549E8259A /* HeaderFooterView.swift */; }; + 267E463DE56F4458A269EB1D2AE79054 /* Realm.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 11CD0690F393036A8C39A6F395DF660E /* Realm.h */; }; + 279A83DC9F2C01F480BC112A20B6873C /* SwipeActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93E450EF6884F59FE08FF8AF0362C88C /* SwipeActions.swift */; }; + 287BE75854BFBB3B49A3CE3268F4E022 /* ButtonRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 126549DC194D6BB04E1151C548284B8D /* ButtonRow.swift */; }; + 2931CE2E87E9B5B714C9B824226F07AD /* RLMListBase.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 91D870DD127FCCA184837D3AA0A3F503 /* RLMListBase.h */; }; + 2D0CAB4DE6EA48416AC86C2CCAE98298 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 14D0A18795829341CE4B7A3C6A1EEEB0 /* Foundation.framework */; }; + 2DDC0F1392ECF9DACDF75AED436B17F1 /* RLMProperty.h in Headers */ = {isa = PBXBuildFile; fileRef = 924FEE607EDFD95455B17DDB02D0923C /* RLMProperty.h */; }; + 2DE681521553036E7A9313A34996453A /* RLMThreadSafeReference.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2CACB94AD762BB71C001B2762B43E660 /* RLMThreadSafeReference.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 2E32FEE068EABDFBF2FEF0B20180BE88 /* RLMSyncConfiguration.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 1D700D1CF120FEA45E20E220BBD986A1 /* RLMSyncConfiguration.h */; }; + 2E97216C1C7F9F2CD9BC52EA234127CC /* AlertRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0F0AF7305F387DC1EB5B808BFDB0C0B /* AlertRow.swift */; }; + 30E7256B41BA0DABAA10CC68D8D7BF53 /* OptionsRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6F999DFB1EB5A441AF482A5FC81007 /* OptionsRow.swift */; }; + 31739A239B975584242C0C5156B29F58 /* RLMSyncConfiguration_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = BA0C8517B53BB218529BE33B0282E3B1 /* RLMSyncConfiguration_Private.h */; }; + 31EA6AF4ACD9318764BD6B4FC777D28F /* sync_user.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 75871C8208172885ED1B2473CD69CFF9 /* sync_user.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 31F19905E1A6E3E8D72A3776E8F6D340 /* RLMSyncUtil_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 2E6D45894756DCCFAF546F9C2EC4CC1A /* RLMSyncUtil_Private.h */; }; + 320C2047153812DE7AB64AD57464857D /* RLMConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = E8A82C9D0BBFA86E32BCC4120D292EF3 /* RLMConstants.h */; }; + 34F518D2BA46F7163960780654DD00A4 /* RLMSwiftSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 78D9249A3C56C111F67D87AAFBD4037E /* RLMSwiftSupport.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 3626B94094672CB1C9DEA32B9F9502E1 /* TaskDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE239D642911B6F39732B46D5AEC203E /* TaskDelegate.swift */; }; + 36E2B195CEEE05968F132C9189317832 /* RLMObjectBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 6DC7EB8E3DE789DDA96E80E38F29D51B /* RLMObjectBase.h */; }; + 37085202D19F0204D0546AC692FC0A01 /* RLMResults.h in Headers */ = {isa = PBXBuildFile; fileRef = 90DB17A7AFC7EB4978CF84E262E454D5 /* RLMResults.h */; }; + 3782E7EBB5E73BA7BE1EC8890FA347AA /* thread_safe_reference.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53168BD4DCE3E1733D2B567714F78D4 /* thread_safe_reference.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 3821AF330FE77D79F58A716829D92996 /* Eureka.bundle in Resources */ = {isa = PBXBuildFile; fileRef = F1F70F6236B9263466A90E34971C9A5F /* Eureka.bundle */; }; + 3B62FBF56D30A265E6B40FDD111696C5 /* RLMCollection_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = D710B0CC80C2FDA501FD4F5D2FC52B2D /* RLMCollection_Private.h */; }; + 3BA9372F8F9A3885FBDC66DFB3536B59 /* RLMRealm_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 28210C7A86CBE8162C3D55C22079D4B2 /* RLMRealm_Private.h */; }; + 3BAB4C4AA768EC0419D5749CB82C75B4 /* RLMProperty_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = EEB08D83627669E22B8E0E7D16CDAFB1 /* RLMProperty_Private.h */; }; + 3C84291DDD10DE22CC5DB09B6C970EA1 /* primitive_list_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A23EF01AB8B863A114F893F1DE53FA9E /* primitive_list_notifier.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 3DAA106B3684A06FEA830E090A697132 /* SliderRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55BF6C9C3259F218D33485F16A52709C /* SliderRow.swift */; }; + 3E0574746ACD3430FBC2E5CBF9B0E0EF /* results_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 529D2CCFB71D0697A0F9F093C4D6FAEA /* results_notifier.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 3E645A4D6950FF94D944EF8DB15E294E /* SelectorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E820F2BE88277EBA52781D57A928B924 /* SelectorViewController.swift */; }; + 3EA100AAD29DAB00C8DCB029A5512C1C /* weak_realm_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49A2B3436DB98A5B3255CDD8ABC660FC /* weak_realm_notifier.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 3EC1ED3912870E8CC346FD831738155A /* SwiftyJSON-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 30AE50683B795C951A63D332AD22223C /* SwiftyJSON-dummy.m */; }; + 3FAD614E68947DA3994519A37F8056B9 /* RLMProperty.mm in Sources */ = {isa = PBXBuildFile; fileRef = CD99ABACAA310323AEDC1AE7A9E65321 /* RLMProperty.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 40AB45ED67B70B7533B640451D1471BE /* network_reachability_observer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 586218F9863E99A2078D4466D65A01D2 /* network_reachability_observer.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 4295A1F2F7F1C35B45BAE99178B4D187 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 14D0A18795829341CE4B7A3C6A1EEEB0 /* Foundation.framework */; }; + 44627784F3ECCC248BAF8B01003A2FEA /* FieldRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2819BE1610A8748120C720DC3AA0C66B /* FieldRow.swift */; }; + 45A30147FA0F0703067D5175F610A00F /* SelectorRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20975C0C353901A50CCED023965AF70A /* SelectorRow.swift */; }; + 46AB04EB4D5583340A65BC43CBFB507A /* RLMObjectStore.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = C36443BD174010DA804B31FBA85C761D /* RLMObjectStore.h */; }; + 46B6797F757C3C01F8B7283C22D50E16 /* RLMUtil.mm in Sources */ = {isa = PBXBuildFile; fileRef = 113684CC953F62A508BFA81EC4AA2D22 /* RLMUtil.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 46E811FE94B21D3D715CE3B737C8E8E1 /* shared_realm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1447446CBA5E861394374981B4CE394 /* shared_realm.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 46F860BC40EB0F3D5E45215C955207C5 /* RLMArray.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 477D374261C345967081120A29C9D12C /* RLMArray.h */; }; + 47010949994F76F8A18C8553448470DF /* RLMResults_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = FD6809E2C37CAAEF32341A838B340ED8 /* RLMResults_Private.h */; }; + 478664BBB23B1EAD57A4A5C76E673252 /* RLMResults.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3E40088A1D0BC7B2FEE5F5BEA221FF6F /* RLMResults.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 478EFB47E9C9D8C6E3580D4EF8C93DE6 /* RLMSyncCredentials.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 80209A2BD2DAA37387B2006851344114 /* RLMSyncCredentials.h */; }; + 47BEC5DEDCA2C08F55E56EEA185641CD /* RLMArray_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = EA80AE3316691FF6213775C016E85D07 /* RLMArray_Private.h */; }; + 48B8426CFA7B8518DC2FD4E82EAFE8BF /* object_schema.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D6AAF42155764E91D966EFE018D4F18A /* object_schema.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 48ED1E1E86AA99B1B89422323D039D47 /* PickerRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0BF74DA8204936480F9E9798F1CA3BC /* PickerRow.swift */; }; + 4AA00E8F043842A1974826BA525CFAAB /* RLMObjectSchema.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 84B59231ACC04A09E7261D7431571B3D /* RLMObjectSchema.h */; }; + 4AECEECAFC2366D7C30D8BD6F84FD953 /* RLMAccessor.mm in Sources */ = {isa = PBXBuildFile; fileRef = DB7795DEDC3688BE80EBE0633A325028 /* RLMAccessor.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 4B77541D5B5F0763633FC49F6460F6B5 /* RLMArray_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = EA80AE3316691FF6213775C016E85D07 /* RLMArray_Private.h */; }; + 4C51D57FD3F184E87A83693D0FC367D8 /* RLMAccessor.h in Headers */ = {isa = PBXBuildFile; fileRef = 64D8D0B21B9ACFD54B1A199C25EB73AB /* RLMAccessor.h */; }; + 4CB3985C8BE626353E4351A9DBA4AA62 /* RLMObjectStore.mm in Sources */ = {isa = PBXBuildFile; fileRef = C1A6B66006D194520803DE174BC1EC19 /* RLMObjectStore.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 4CF684A09D3BDDA366B0856F77227215 /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45816D1BAA763492080FC611A65F04C9 /* Protocols.swift */; }; + 4CF76423EFD0B8B272AABDC7B39F8F5C /* RLMSyncUser.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 6FEABED8562208C6D084A3C7C1B5B913 /* RLMSyncUser.h */; }; + 4D3F908669ABA61E3D4054A297F60755 /* RuleRequired.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26E6FB2127BE3D720B17AFD1B323E0C0 /* RuleRequired.swift */; }; + 4EA55BBBB181C6E3A739759236173A18 /* RowProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBFCD450C7395E074EBFDD409E511993 /* RowProtocols.swift */; }; + 4F7EA69A18363E1D36DAB81FA099937B /* object_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 24E2229A2BA4D575C6E5A758F693A40F /* object_notifier.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 4F8B3D0568E65D9C804FB9621F68E192 /* GenericMultipleSelectorRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA6F7A4BEB49F3A40F3463280C8AA5FD /* GenericMultipleSelectorRow.swift */; }; + 50FAECEF7E678D9282B5D9AAB29953D9 /* sync_metadata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7AB110AA2342D66D7CC1EC5DE3A9AF95 /* sync_metadata.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 5115BF24865F21844361BD1899FB408B /* RLMSyncCredentials.h in Headers */ = {isa = PBXBuildFile; fileRef = 80209A2BD2DAA37387B2006851344114 /* RLMSyncCredentials.h */; }; + 5189820E69253506FD7310042AD7926F /* collection_change_builder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6C8D84EC7A695F0DFDA1DD6F69A82FE0 /* collection_change_builder.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 53160B4A55556EC639CF1E4ACE5FE784 /* RLMAccessor.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 64D8D0B21B9ACFD54B1A199C25EB73AB /* RLMAccessor.h */; }; + 5387216E723A3C68E851CA15573CDD71 /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1CA9182A041E4565AB6FEA21C6D2421 /* Request.swift */; }; + 54F4FD105F0DC99B6EEB538162AEFD30 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88A609AD0DADD55736F3321FBE23117E /* Property.swift */; }; + 5533F297F16DE0A8BB487D2A1CA94395 /* DecimalFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A22A60BC550E9C333E9F1B38A5505AA9 /* DecimalFormatter.swift */; }; + 568184DF93AC3C9DFB2F24557395B7DE /* Realm-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 11CA426804A9BB072D81CAA11AD1F09A /* Realm-dummy.m */; }; + 587CF5E6828EAEFCB1677374EDEC4B72 /* RLMPlatform.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 7A8920FC9084B4C9A81116734A9749A0 /* RLMPlatform.h */; }; + 58BCBF1A71B357291C76E826CC4D80C3 /* Aliases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 800126034A32DA13013907A513953FE1 /* Aliases.swift */; }; + 59AFC3E058DFA888D3336C284D9ECAC5 /* sync_config.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 87C392FB2A1558DFB70FDB1D384A0218 /* sync_config.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 5A141D1AFC1301ADB773261E7B133090 /* SortDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF71550B6E0D71EE6264D5EFB88B3B16 /* SortDescriptor.swift */; }; + 5A3516009F452DE4C52D34D9E1F55F6E /* DateInlineFieldRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43F37F19567EFD7F2634E392C4938D43 /* DateInlineFieldRow.swift */; }; + 5AEF2A6ADBB063F04EA765993A579912 /* RLMPredicateUtil.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0BEEC4B884A769315B12C502799ED7D9 /* RLMPredicateUtil.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 5B55B9BCDCEB99090615AACC36439906 /* Optional.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6D52004D92F7AF737CB6373645DB781 /* Optional.swift */; }; + 5C1834BDC03355D57DB77D07B07A117D /* PickerInputRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40DC828D14E27D6671A3F0DF8EFA3FEC /* PickerInputRow.swift */; }; + 5C4A031D35521F124639526D9CC59A18 /* SwiftyJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83DB289513B9755814CCED9C686CFCED /* SwiftyJSON.swift */; }; + 5C4B188E302E48361E80ED6EA892C471 /* RealmConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5148E4A64F089541BBC2DDEDFC60CBD7 /* RealmConfiguration.swift */; }; + 5CEC5970C4A3C1FEA706B041EE93925E /* NavigationAccessoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6ED883282453F89464A3E2B47B0D90A6 /* NavigationAccessoryView.swift */; }; + 5CF10C1EDB9B4BA4C6909DA065E15F75 /* RLMObjectSchema_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = AFA7FB6D6175EBF3AA8DDB331F9C8083 /* RLMObjectSchema_Private.h */; }; + 608747D1FED76A804DD80BB89DD74122 /* RLMObjectBase_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = C3D5A37CD39E1E15FB43F26E2D9F7786 /* RLMObjectBase_Private.h */; }; + 61200D01A1855D7920CEF835C8BE00B0 /* DispatchQueue+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABA23A5018A81ACC4DC60114F737BDAA /* DispatchQueue+Alamofire.swift */; }; + 62D97A11B64C00B0EB740B019D5EE504 /* RLMCollection.mm in Sources */ = {isa = PBXBuildFile; fileRef = D386F72C81BB35F7759273D64401BA4E /* RLMCollection.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 62F65AD8DC4F0F9610F4B8B4738EC094 /* ServerTrustPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 097857138D9821D34C7FCB860A83C454 /* ServerTrustPolicy.swift */; }; + 63BF963098CCCDDC24BA51EC9604FC5C /* SwiftVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = B06D8074680CFCAEF9159D23514BC4AE /* SwiftVersion.swift */; }; + 63D110301D03DE07431C773697A7911F /* RLMRealm.h in Headers */ = {isa = PBXBuildFile; fileRef = EF7F18C6A90F1DD1BE53BCE4102A40B4 /* RLMRealm.h */; }; + 640270FF7B7CC7A4445CB23EBEEF1DEC /* RLMRealm+Sync.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = F42A8422312B929411F5B93DEEEFA7F8 /* RLMRealm+Sync.h */; }; + 65DBB00047899A34D9DF1DDFB389FF66 /* RLMSchema_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 57C9F248C0AEC8E0A677F1A588EA291D /* RLMSchema_Private.h */; }; + 6669F28B1A2C7D31D1220BFE607F5FE8 /* list_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 194A2BE505076878605C44E278887A41 /* list_notifier.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 669DF2CAAC0A642953E8577BDD08645B /* RLMSyncUtil_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 2E6D45894756DCCFAF546F9C2EC4CC1A /* RLMSyncUtil_Private.h */; }; + 696DFD8B1B417C7392CC52C46FCEA6D6 /* RLMListBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 91D870DD127FCCA184837D3AA0A3F503 /* RLMListBase.h */; }; + 6AB1CD624CA00666B5D90F01CA9956D8 /* RLMRealmConfiguration+Sync.h in Headers */ = {isa = PBXBuildFile; fileRef = CB5C94E9B607AB4BB4B946482BFA6B8F /* RLMRealmConfiguration+Sync.h */; }; + 6B160DB4D477853EB91FE5E2E4B36106 /* RLMRealmConfiguration+Sync.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = CB5C94E9B607AB4BB4B946482BFA6B8F /* RLMRealmConfiguration+Sync.h */; }; + 6B46F29203A7BED5054EDC9EC4BB1546 /* RLMListBase.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 91D870DD127FCCA184837D3AA0A3F503 /* RLMListBase.h */; }; + 6B8B6D0E5918C6FB627EE302EC3E648E /* RLMSyncPermissionResults.mm in Sources */ = {isa = PBXBuildFile; fileRef = 04E903ADF8F796BCAD79F12C0E094A5D /* RLMSyncPermissionResults.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 6C1EB5D1744D0E29EA224745A24F0C6F /* uuid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 655EFBA9BCFB51B0DAEC75CC202C03CD /* uuid.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 6C3CCBDE76FDB1C55A8AE0980BEA0CF8 /* system_configuration.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DC698CDA7D7633277D18E4E2DDFBECA8 /* system_configuration.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 6CF8302A330790C27A1C7B32EB57B2AF /* collection_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D1091D628F3A1E41BA2BCD0AAD0190A3 /* collection_notifier.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 707A0E5F7DBC968D1340A1FF2B08BFD7 /* ThreadSafeReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0447FED55AE17952ABB23A6B30DE55AB /* ThreadSafeReference.swift */; }; + 71E24C6488405AB4259428CB5DBBBBDD /* placeholder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 032E3F03CAA28A8D926A754A61106180 /* placeholder.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 73B9C996AED49ED7CF8EC2A6F1738059 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 14D0A18795829341CE4B7A3C6A1EEEB0 /* Foundation.framework */; }; + 74148E1FE3BB750800D605008ADAD720 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E0F53489BE5523FA7EC41E425BE6733 /* Operators.swift */; }; + 7630E6F54BD665C6CD942A4983349B4A /* RLMSchema.mm in Sources */ = {isa = PBXBuildFile; fileRef = BE2AD210BCB92444548FF2D08C23D017 /* RLMSchema.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 778953B600956A438009F452199B67EA /* RLMSyncManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 588E52B881F749AF8A57755A955AE4FC /* RLMSyncManager.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 779464C25D52447D04DD0A6C8A3CC40B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 14D0A18795829341CE4B7A3C6A1EEEB0 /* Foundation.framework */; }; + 781FA00782F7D7FC5FC7B37465D3DD0C /* RLMSyncSubscription.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D1D46BBFB6D9B74339D10D0FC54F60F /* RLMSyncSubscription.h */; }; + 79A0F3AC5DC49A981CCC5FE6EAE148C2 /* RLMRealm.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9F51A7E1B01B2BB97ADBB863BD6D4302 /* RLMRealm.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 7B5FE28C7EA4122B0598738E54DBEBD8 /* SessionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53892895AD736DB83F24CB66C8BD5742 /* SessionDelegate.swift */; }; + 7C44A2AD32395ECFCAD94ABBD590130E /* DateFieldRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E81A5B9FF8E4836044E1AB76D5B216C /* DateFieldRow.swift */; }; + 7C57457B4CBFF0145014D87782AE7D34 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 14D0A18795829341CE4B7A3C6A1EEEB0 /* Foundation.framework */; }; + 7D8CC01E8C9EFFF9F4D65406CDE0AB66 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B578B60A62261F86478ACD049E8E436 /* Result.swift */; }; + 7E964866F1C3B481A83929A16D6AB3E4 /* RLMSyncSession.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 46420844EAA978DC4528A13BB970D63B /* RLMSyncSession.h */; }; + 7ED7B21E88EDE7A0293EC6EDEC6A9DD5 /* SegmentedRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBDB3F8F7215ED51DA8A86CF19D1AA9C /* SegmentedRow.swift */; }; + 7EF3713799FE9AC463D4BE55EC128231 /* RuleEmail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1203D759E1683FCE548BB3DDD477B8BE /* RuleEmail.swift */; }; + 7FB875528634DFE291B4B6CC882655E9 /* RLMSyncPermission.mm in Sources */ = {isa = PBXBuildFile; fileRef = 10450524A1F8D00FF064E5B7E2810EFD /* RLMSyncPermission.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 7FD640D5AB6F39FFF696D6F7E2C05959 /* RLMObjectSchema.h in Headers */ = {isa = PBXBuildFile; fileRef = 84B59231ACC04A09E7261D7431571B3D /* RLMObjectSchema.h */; }; + 8099CC4DA6F69D06755393EDB5BC9893 /* RLMSchema.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 15557EEAE9332FBEF162AC42204AB072 /* RLMSchema.h */; }; + 816D2FDE9FC8575B1FA4515AA16233C4 /* RLMResults_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = FD6809E2C37CAAEF32341A838B340ED8 /* RLMResults_Private.h */; }; + 844ED9E2D730BD83D6BC206D336F3135 /* RLMProperty.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 924FEE607EDFD95455B17DDB02D0923C /* RLMProperty.h */; }; + 848D93A72FA1AC760E63B413CBAA6DB7 /* RLMClassInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = CE4D9A2CB050B8F48137F0CB588079D7 /* RLMClassInfo.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 84C1EEEFE6BAEB404BDF1DFE80B0103A /* Cell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59EE81AEC41C45CC0D5D8D10B5E2FE14 /* Cell.swift */; }; + 865B7CA0F73C124C27871A17AC8B125A /* NSError+RLMSync.m in Sources */ = {isa = PBXBuildFile; fileRef = 3318D76ECFB236EFC0BE76A4FB42F14C /* NSError+RLMSync.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 8768811AE380F085C0D6693993409B18 /* RLMObject.h in Headers */ = {isa = PBXBuildFile; fileRef = B3886B0BB14B2EF5C4262922ECF3DE2E /* RLMObject.h */; }; + 877A0A159511077573CF194DF28D8037 /* SwitchRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95EF6D5795A0383AB2DC902E386A774B /* SwitchRow.swift */; }; + 87B6F6EFDC0598100FCD185D779845C3 /* CheckRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC63F855BA43317E954BD3FE6CA57B9 /* CheckRow.swift */; }; + 8855C61A942C3B3B3B577420EF08AB79 /* RLMOptionalBase.h in Headers */ = {isa = PBXBuildFile; fileRef = BB2B4D0F33C6541E253E580A9771D48B /* RLMOptionalBase.h */; }; + 89EBE6099502CFF9662F446DCC6A4482 /* RLMObjectSchema.mm in Sources */ = {isa = PBXBuildFile; fileRef = 702440DC1A628016068AF5189F2DC8C9 /* RLMObjectSchema.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 8C5A01880B66C01C84223F4EF4C474BE /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 523DBDBC9549556983C47F1BC098034B /* Helpers.swift */; }; + 8C7FD8DFA011246E17D3C7E85E346314 /* schema.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 43198200D81D6B690D170883B3D376ED /* schema.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 8C9745983CF09DBCE1962EBA38AABC92 /* CellType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31FD96ACD62FF946B63DD498461E60A2 /* CellType.swift */; }; + 8D4D3BAF2CA1B006A5A166278D0493D6 /* RLMListBase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5D58DDDAF8A6088DF40E48B6DB866494 /* RLMListBase.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 8E930C3BFE5E90BB07909F4AA2F8DDFE /* PopoverSelectorRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4214080B2BE7243648C4456549CE8182 /* PopoverSelectorRow.swift */; }; + 8F194A5CD188F7E0E82B1C12621450E3 /* RLMSyncUtil.mm in Sources */ = {isa = PBXBuildFile; fileRef = F7B108A8D73BBF19C20D7D63650FDE14 /* RLMSyncUtil.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 914DEA5309A0973CC4B0E4FE333CFD3A /* DateRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93D93C3ED858076114B56C5751004996 /* DateRow.swift */; }; + 92557580872DDD0A6891419D1DB8BE2B /* ListCheckRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = E99EA87D64192E461F9D10E54BA19CEF /* ListCheckRow.swift */; }; + 9779299ABD0BEA143A63517A8847E2A5 /* RealmCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79FAFB0B673F3C800E0154FDE6F2BF79 /* RealmCollection.swift */; }; + 982E9EF335092CDDC4FC32BDD5DB15EF /* TextAreaRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51DD1B1FA32059B7169BC128A060A5BC /* TextAreaRow.swift */; }; + 9844C9BB8B73E745AAC35278010B7762 /* MultipleSelectorRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = E59898BDF46BA9F2CBE2DF02E4B92D42 /* MultipleSelectorRow.swift */; }; + 99357F25FAAA58D881302ADAC7BB6F4B /* RLMSyncSubscription.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 5D1D46BBFB6D9B74339D10D0FC54F60F /* RLMSyncSubscription.h */; }; + 9A41F5A03BD9A244943CC44B7AE5BD50 /* RLMSyncPermission.h in Headers */ = {isa = PBXBuildFile; fileRef = 55883507AB23386BC49FDAA28C3C368A /* RLMSyncPermission.h */; }; + 9B3EDD6032DC60C5993FBA5C98E4B7A8 /* RLMRealmConfiguration_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DE71E71F838918B77F921956DCAEAA0A /* RLMRealmConfiguration_Private.h */; }; + 9C0367D5799033228FE81CD8BB76947C /* RLMSchema_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 57C9F248C0AEC8E0A677F1A588EA291D /* RLMSchema_Private.h */; }; + 9C6FD8BBBFAC7CB6AC73E486753F0A61 /* RLMMigration.mm in Sources */ = {isa = PBXBuildFile; fileRef = E2DF58383A66F988377F979F6E35039D /* RLMMigration.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 9C84133A55BEF2AB3E00326F2F93BB62 /* Sync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03AA6506C5ACCF14F540BDFAFF02204F /* Sync.swift */; }; + 9DD39D0018BD08D5A72089ADF663F85F /* SwiftyJSON-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = B5DAAF083C1981C7AC6747B2D61DC84B /* SwiftyJSON-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9ED1CB54A6C6476AAE61ACB8C2775527 /* RLMObjectBase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 649F42D8A431E971B7E9584E04BC442C /* RLMObjectBase.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + 9ED2BB2981896E0A39EFA365503F58CE /* AFError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB905F3B3B079C5759DDF3A00470EE4 /* AFError.swift */; }; + 9F26EAAD38045F100581AE859D510859 /* Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA691D57062AA682DE09170C69A1406D /* Core.swift */; }; + 9F9C4CF9CF78B1D3999F73F71D5EFA23 /* DatePickerRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA0B007065C889BDFFC676FEFBB11F6F /* DatePickerRow.swift */; }; + A00D26EC4C0564550CFD698E63D66C53 /* LinkingObjects.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1898E6460BE4062611AF6E8820C8E0BA /* LinkingObjects.swift */; }; + A295E57EFDBBDBA3A4D79815A7FA35DE /* Realm.swift in Sources */ = {isa = PBXBuildFile; fileRef = B352EAE0150B12A4EBDA9B7420273246 /* Realm.swift */; }; + A2A6F71B727312BD45CC7A4AAD7B0AB7 /* NetworkReachabilityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 864B328D62AF03B02276BECCFDFF9040 /* NetworkReachabilityManager.swift */; }; + A42730CE505B4B152DC3BD016CECB938 /* Form.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D661C888B638570C004F215A0184808 /* Form.swift */; }; + A5505D42DDC179396282B1CC880340DB /* RLMSyncConfiguration_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = BA0C8517B53BB218529BE33B0282E3B1 /* RLMSyncConfiguration_Private.h */; }; + A7B38585E110183B601BE3FDCE7CF24F /* TriplePickerRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64CE84F06C000C1E7D1BAAE4FA21C03E /* TriplePickerRow.swift */; }; + A8BCC58476C92EB4E4AAD3344AA41317 /* RLMProperty_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = EEB08D83627669E22B8E0E7D16CDAFB1 /* RLMProperty_Private.h */; }; + A91E02EB3D436D81C1F7F4BDAD695DE4 /* FieldsRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF9E9D7F40ED5AAC52781E5915DFB4B9 /* FieldsRow.swift */; }; + A9970438CE8FC3C3832BAEB2B0B8338E /* list.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6024BE960393D401008CED073BF041D5 /* list.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + A9EEEA7477981DEEBC72432DE9990A4B /* Alamofire-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 45AB90ECD8AB5DD612FB49FDD020BAEE /* Alamofire-dummy.m */; }; + A9F1AF3B8BEFC83ABD3B631AA34A321B /* sync_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49AB68E08DFCC8BB5588683D51F70C42 /* sync_file.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + AA86D52378C7D8A854461F1041F6F717 /* RLMMigration.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 0F620F20591BA62E035F169DE1CE3FE5 /* RLMMigration.h */; }; + AA910A5A283B3593AEA0185CED8E86ED /* RLMSyncSession.mm in Sources */ = {isa = PBXBuildFile; fileRef = B6979907A286F1FF396BC34194D33F73 /* RLMSyncSession.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + AAF5D5D732267565F122BF98C1838B9A /* RLMRealm_Dynamic.h in Headers */ = {isa = PBXBuildFile; fileRef = 8EF03B0A1821B57A4D813FC44E6DA4DB /* RLMRealm_Dynamic.h */; }; + AB20E8801433DF2706C58F984719275F /* RLMCollection.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 18EA699644630EFE9C3F737E1D7AA22A /* RLMCollection.h */; }; + AD76C9D1DA235A363D2606A874E565F4 /* Pods-Anyway-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CA9E23211534D85FD7AC2B114442295 /* Pods-Anyway-dummy.m */; }; + ADA7C9C45375006ACA49439ADB1BA9E7 /* RLMUpdateChecker.mm in Sources */ = {isa = PBXBuildFile; fileRef = F7172D19BC41F018204B6397B905F0CE /* RLMUpdateChecker.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + AE1EF48399533730D0066E04B22CA2D6 /* SessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9458CF5DCA16A9827FA2C7D0DC6812FE /* SessionManager.swift */; }; + AE34EC79435E2F68A678985572D16FEE /* SelectableRowType.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA74C2E8C385452EE0A250F6225371B0 /* SelectableRowType.swift */; }; + AE68B96A894BB61D13DB1DBE19DFED69 /* RLMArray.mm in Sources */ = {isa = PBXBuildFile; fileRef = BB69FC881A589171D4EA27D5D89DBAF3 /* RLMArray.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + AFD326BE84ED28DC8621656AF856BDC1 /* Realm.h in Headers */ = {isa = PBXBuildFile; fileRef = 11CD0690F393036A8C39A6F395DF660E /* Realm.h */; }; + B113AA249649D0407F71C8360EB55084 /* RLMSyncPermission.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 55883507AB23386BC49FDAA28C3C368A /* RLMSyncPermission.h */; }; + B193E96033D067BA3D0133C9883A4EBC /* RLMOptionalBase.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = BB2B4D0F33C6541E253E580A9771D48B /* RLMOptionalBase.h */; }; + B33EB368DBCFCCCA78BA43D770BCB626 /* RLMOptionalBase.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = BB2B4D0F33C6541E253E580A9771D48B /* RLMOptionalBase.h */; }; + B39E3EC7DD02DBBEB8191E4907B182F2 /* BaseRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78DE9922E322A9120D18498BFF31B499 /* BaseRow.swift */; }; + B3D4C29C195EC854278DE1C3604C85B8 /* PickerInlineRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51D319F3644A3B5368DF418F82C60851 /* PickerInlineRow.swift */; }; + B467EC1A1428B231FDF2FFB190E2C6AC /* RLMRealm.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = EF7F18C6A90F1DD1BE53BCE4102A40B4 /* RLMRealm.h */; }; + B4E2C479A4F5AE6B623358BA627CBF7E /* ActionSheetRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5FD6F5D01BF40B3873D41F079FE5595 /* ActionSheetRow.swift */; }; + B54D247B1E7212BA3402333CC499B1AC /* realm_coordinator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E40D41B07E90F49DF435B9EB8603D8E8 /* realm_coordinator.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + B56DA7CCA71E56DDA822E43471168DD5 /* DoublePickerRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25B113DF62FF81079C8BFDA88738D5B5 /* DoublePickerRow.swift */; }; + B57FFE0778AE7613074B8D785EE3D125 /* keychain_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7F525C17DDBB7FA46D49579145BDEDFD /* keychain_helper.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + B65FCF589DA398C3EFE0128064E510EC /* MultipartFormData.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0664560BA9CC6CBF5068252B6986EE9 /* MultipartFormData.swift */; }; + B74A338ED05D70855ED500375705FA25 /* MultipleSelectorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 751759ECB55AEBE77F10C2ACBB5FC5C7 /* MultipleSelectorViewController.swift */; }; + B758518039F59EE5954420C880AD99A5 /* InlineRowType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7634052748874DCB824BA021EAC0031 /* InlineRowType.swift */; }; + B958E5C3EE5DE7263608B4AFE58C12C7 /* sync_session.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9486BD497E3820245DCFD08D07285B0A /* sync_session.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + B9E2ABEC49C60A27FB201EB15BEA4FE9 /* RLMObjectSchema_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = AFA7FB6D6175EBF3AA8DDB331F9C8083 /* RLMObjectSchema_Private.h */; }; + BBEFE2F9CEB73DC7BD97FFA66A0D9D4F /* Validation.swift in Sources */ = {isa = PBXBuildFile; fileRef = B726C060B1B3118F74436CE2D554AB90 /* Validation.swift */; }; + BC159338C06BCE13FCB2218A76131CF1 /* RLMNetworkClient.mm in Sources */ = {isa = PBXBuildFile; fileRef = B6D34AA8D31CDEF7005ED43E1DE58E5D /* RLMNetworkClient.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + BC4AD0347C5C33B53016C97AFEE1AF63 /* PushRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D4C701C8D05720E98DE9B454811B7C4 /* PushRow.swift */; }; + BCF869F848BDFB858BF8839651612BA4 /* Section.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A6499C4A8C3B77D08A8CC89C3A2CD9 /* Section.swift */; }; + BE5C67A07E289FE1F9BE27335B159997 /* ParameterEncoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 986CA011E61CAC25F855201406097F66 /* ParameterEncoding.swift */; }; + BE8036F2B830A87E24FA93058208813C /* List.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EEAD778E47291DC5CA392CEAD168A93 /* List.swift */; }; + C02E824B5FA3AE349B3BF707777CE598 /* object.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EE7100D4160D470F3B510553F389469F /* object.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + C2DC4AD2F3995AA81707794689F8CF74 /* binding_callback_thread_observer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D1B7F7FE8842DA2A2915B46BA27D96EF /* binding_callback_thread_observer.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + C2FACC33902E82500AB1584B05079027 /* RealmSwift-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D114CB4B4F86FEAD7367E0C96B5C20E /* RealmSwift-dummy.m */; }; + C36F4DCD201581A1BBBAA6A911B8782A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A3E10A84830BB961553A10D85557C35 /* UIKit.framework */; }; + C3D4AF46447ABEC455E52787E49067B0 /* RLMSyncUser.mm in Sources */ = {isa = PBXBuildFile; fileRef = 14E99727ACE0BB6DB0B22DB41EE62C2F /* RLMSyncUser.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + C5BBAB3319B59F0C6A4797FEC3F2CB2D /* RLMRealmConfiguration.mm in Sources */ = {isa = PBXBuildFile; fileRef = 001110A6560B90CAE1EB189F2495F8D1 /* RLMRealmConfiguration.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + C806286B29BA9B21543D80642E4BA9C3 /* RLMSyncUser.h in Headers */ = {isa = PBXBuildFile; fileRef = 6FEABED8562208C6D084A3C7C1B5B913 /* RLMSyncUser.h */; }; + C8682BB725C720E6B2A85D5B8778DCF3 /* DoublePickerInputRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00A48D5F9653E688D856A41C12A94455 /* DoublePickerInputRow.swift */; }; + CA0C6EED01AEF9503F563F22152B897A /* NSError+RLMSync.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DC356638CEC2013EB2AA5C48324795 /* NSError+RLMSync.h */; }; + CB6D60925223897FFA2662667DF83E8A /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A42EC471968E262A3E4B2D87D97EABC /* Response.swift */; }; + CB7893F1F8C12A415F474AAEA7360CA7 /* Realm.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 246EB3837726573E0FFCC0A9481009D3 /* Realm.framework */; }; + CBDE2E90628F1C354B6E3CF645CFBAED /* RLMSyncConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D700D1CF120FEA45E20E220BBD986A1 /* RLMSyncConfiguration.h */; }; + CD2F4121E14584F475298E058862BAE5 /* Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F719281B4D3F905F5A67612C4BA14A2 /* Schema.swift */; }; + CD38830929E27B57CD57C6322A789B5A /* RLMCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = 18EA699644630EFE9C3F737E1D7AA22A /* RLMCollection.h */; }; + CF6CAA75B59F5CF308992849F688F6FE /* RLMRealmConfiguration_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = DE71E71F838918B77F921956DCAEAA0A /* RLMRealmConfiguration_Private.h */; }; + CFFEC41F2E81F8C8277AD6FD00B447E2 /* RLMObjectBase_Dynamic.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = CC33867D8B53A2679CF5D156B7132E9B /* RLMObjectBase_Dynamic.h */; }; + D0155F699048D635CFDE7B6400B2276D /* RLMManagedArray.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6649B98D9AD056DE4CFFA3D8A37F8925 /* RLMManagedArray.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + D34F77832B9FF78147510224EB7F6B64 /* external_commit_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DB06C6D64D9A14CABD9501EF856F74C3 /* external_commit_helper.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + D400533601A5D7376A13F567908850C3 /* RuleRegExp.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2D46A47CEAE656BFDBD8C38050C1226 /* RuleRegExp.swift */; }; + D4E45F75BD63B29682B6C23CF9C3C662 /* work_queue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ED7F0E7D00FF705E8A813AE82E4FB94F /* work_queue.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + D6E282CFDA0176397A3E0B6F28A7FEA3 /* RuleURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14C503A15D13A17BFC554427E7F9A28F /* RuleURL.swift */; }; + D7A4E12989499842EBB17D60A32E663E /* ObjectiveCSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10E3D436E5AC2DC7953C3BF2822B8F69 /* ObjectiveCSupport.swift */; }; + D8EE76C28F804327C31A2A319544E619 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF71C7FE31DAA56DDEF5093CEFFB2961 /* Error.swift */; }; + D9BDCC52C9C1CA379B17CC43D492AEBA /* RLMSchema.h in Headers */ = {isa = PBXBuildFile; fileRef = 15557EEAE9332FBEF162AC42204AB072 /* RLMSchema.h */; }; + DA6591EDA5CC079BD109D60FBC09C86F /* RLMObject.mm in Sources */ = {isa = PBXBuildFile; fileRef = D59D03A16268D980788997D03C5F8D89 /* RLMObject.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + DA72EE1BEECBFC3AD445F07F8841B779 /* partial_sync.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 327C13A5E90EFC1A5BF2C497A34D014A /* partial_sync.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + DA8619EDE77C518675E4C03ECF8C7785 /* RLMSyncUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D7E4F5987926F2C2BC25B00EA185D7A /* RLMSyncUtil.h */; }; + DAA7B5BCD613442490340F81AEDF3602 /* RLMSyncUtil.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 4D7E4F5987926F2C2BC25B00EA185D7A /* RLMSyncUtil.h */; }; + DC99FD7754461B54AB9DE02D686930D4 /* RLMRealmConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 558D13F22302404CE3C9B913877AE37A /* RLMRealmConfiguration.h */; }; + DD7D0FB52D78BFA107C8F63F80F93A2D /* RLMSyncSessionRefreshHandle.mm in Sources */ = {isa = PBXBuildFile; fileRef = 175A9E8928769E05D3AD017C36A4F8E2 /* RLMSyncSessionRefreshHandle.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + DF1E2EE1590F5AF0A383E45F0EB8592F /* RLMQueryUtil.mm in Sources */ = {isa = PBXBuildFile; fileRef = 344A2CB7F04E410D131FD5DFDC3E5C40 /* RLMQueryUtil.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + DF1FF7E623ECEC3F3AAD33544C48EBE2 /* AlertOptionsRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 187B02672F278FDAB7F49040F27FF40D /* AlertOptionsRow.swift */; }; + E21CB4DBF97C8B27D2C70B425C7A42EB /* RLMObject_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 1BE4DDB16CBE64F4A80116FA616B5E61 /* RLMObject_Private.h */; }; + E4D21C722D1855810159C19B14AA81DE /* RLMMigration.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F620F20591BA62E035F169DE1CE3FE5 /* RLMMigration.h */; }; + E504CADCE3A2F5EE67E1D57E36BFBFFE /* RLMObjectBase_Dynamic.h in Headers */ = {isa = PBXBuildFile; fileRef = CC33867D8B53A2679CF5D156B7132E9B /* RLMObjectBase_Dynamic.h */; }; + E5ED63D2E26F31E61195EBECC3CF9564 /* RLMResults.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 90DB17A7AFC7EB4978CF84E262E454D5 /* RLMResults.h */; }; + E648A4D608F1E8B6F6104DD673F37382 /* RLMConstants.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = E8A82C9D0BBFA86E32BCC4120D292EF3 /* RLMConstants.h */; }; + E8AF429EAB62FDC15EFFB474DFC8B20A /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B8CDAC6FCDFA06F5D625331F21F5F7FB /* Security.framework */; }; + E9EB37F7808C9484BA6A07AA81CA0387 /* RowType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0947500D02EC4BF07CCC37BED5C229EF /* RowType.swift */; }; + EA7673C8C824BECA5901AACDB3A9BAC7 /* Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C81565305966867BC29F784D4A855B6 /* Util.swift */; }; + ECAC9E27655B03ED369DEE7A147F841D /* RLMSyncConfiguration.mm in Sources */ = {isa = PBXBuildFile; fileRef = 10EB8EE4D5BCC0493710CB96CC7CBA64 /* RLMSyncConfiguration.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + EE90DC769A183F60D6B815E3507EF8D9 /* RLMSyncManager.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 03330053F888BD923BF4C4CD4C46800C /* RLMSyncManager.h */; }; + EF5C1615F952354B5A9F02E5F1D4ED39 /* Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C933F380DD2F897B5294CD6BC830C9 /* Migration.swift */; }; + EFA9D9000808D5BB24FE8CF33E085A0E /* StepperRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D2FC371C278EA6E7577D95C098247BC /* StepperRow.swift */; }; + EFD264FC408EBF3BA2528E70B08DDD94 /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 296CB55358BAA7E6D7F3ED0162B610EF /* Notifications.swift */; }; + F0B9289FF41C540C4800B15E74538E5B /* SelectableSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01986AB41A7D9DFFB3AC8DF744D2008 /* SelectableSection.swift */; }; + F16B5A728A6D3560BB6766EDF4A5D087 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 14D0A18795829341CE4B7A3C6A1EEEB0 /* Foundation.framework */; }; + F1DE0BB5E1E6C7CD3A266C5E2B6648CF /* index_set.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 124D4BADF34C3392E8B1E56EF4F2A663 /* index_set.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + F2361F1BB0755C6F76FD41C9EF958ADC /* RLMObjectStore.h in Headers */ = {isa = PBXBuildFile; fileRef = C36443BD174010DA804B31FBA85C761D /* RLMObjectStore.h */; }; + F3EC6A7ED8D205C0D2FE2D4462D9A5D4 /* PresenterRowType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C28F3F6AA1DB3A5174EE5A62B1F2A3CD /* PresenterRowType.swift */; }; + F5B29BE3B9A66D09585475EDC6C68725 /* RLMObject_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 1BE4DDB16CBE64F4A80116FA616B5E61 /* RLMObject_Private.h */; }; + F64E08F16FA9364EF87792B6283DA7FC /* RLMObjectBase_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = C3D5A37CD39E1E15FB43F26E2D9F7786 /* RLMObjectBase_Private.h */; }; + F66996C4CB8FBC49F236B864CB21B78C /* RLMSyncManager_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B57A881AACF13C810FE8799270BC239 /* RLMSyncManager_Private.h */; }; + F6BECD98B97CBFEBE2C96F0E9E72A6C0 /* ResponseSerialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E9232B57FCBE99DC654345273A61422 /* ResponseSerialization.swift */; }; + F6C43E1F6A098750EE456A04E46565DA /* RealmSwift-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 176E8EC2BE2AEC13B33AED131EFC8FC1 /* RealmSwift-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F6F450D42B629A0DB2656A45C27104E5 /* RLMConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = A7BCA7A83702D9E86CDA34631864173C /* RLMConstants.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + F7B5A8CD4BC71DD2FEBCC0E4FF3CB6EC /* RLMRealmUtil.mm in Sources */ = {isa = PBXBuildFile; fileRef = C55AD46B8ACAF57640C65CA2745B13BA /* RLMRealmUtil.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + F8B3D3092ED0417E8CDF32033F6122F5 /* Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C0ED9FAB960CDD538A5C953B92F74C8 /* Alamofire.swift */; }; + FA036C2A3039E362C114476EFEE4A2AF /* RLMObservation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 25456002CE8D3E5F3359AF03BEA449D4 /* RLMObservation.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + FA9F82B0CF2BD9866010CD559772A554 /* RuleEqualsToRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E205473F66611EB373B5AD7DF083FDE /* RuleEqualsToRow.swift */; }; + FC53556EE2B2DED63AB57C1D739250FC /* sync_permission.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ED2C32B64ED0E893C1E80242A025A8B6 /* sync_permission.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; + FC5F8EA3DC8F60DB05E902A96EB4A14B /* RuleLength.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C59C69F2C46DB368087816142770B5E /* RuleLength.swift */; }; + FCEEBA45523D3EDA2F681A06F92038DB /* transact_log_handler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95645FF346EE5177388A4C1D971F81E4 /* transact_log_handler.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.7.6\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -312,7 +324,7 @@ isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; - remoteGlobalIDString = 675A114988AE198732ED4AD726408964; + remoteGlobalIDString = 0F32982340C6013DA81094C00BB2C7B5; remoteInfo = Eureka; }; 6F5EC8ED9F35A3244450AA4BC781455F /* PBXContainerItemProxy */ = { @@ -339,352 +351,365 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - 274115BD2ED7B62A7D2C2EC1A610F9F1 /* Copy . Public Headers */ = { + B1DFE0103C0A91B97C3BD945D05B4156 /* Copy . Private Headers */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; - dstPath = "$(PUBLIC_HEADERS_FOLDER_PATH)/."; + dstPath = "$(PRIVATE_HEADERS_FOLDER_PATH)/."; dstSubfolderSpec = 16; files = ( - A380C8BB57E05BA91CD7B44756107E55 /* NSError+RLMSync.h in Copy . Public Headers */, - 5FE4EE3ECEE5FA37EBAA1A1F33927EFF /* Realm.h in Copy . Public Headers */, - 4664D8E7AA73A05367718FADC48028BA /* RLMArray.h in Copy . Public Headers */, - 6DA0179B88EED766667D1171D035FF83 /* RLMCollection.h in Copy . Public Headers */, - EFC0F73130B1BB595C497FAAD41DC21C /* RLMConstants.h in Copy . Public Headers */, - 690FCBD7E1A1D43E946316682D04B6B1 /* RLMListBase.h in Copy . Public Headers */, - FC7EFA7C36B6FA31AD54ED04AAEB280D /* RLMMigration.h in Copy . Public Headers */, - B24E96BE6BDBD2217A6F798A1DF2A134 /* RLMObject.h in Copy . Public Headers */, - 3ADB85DDB88CA252FE72DED08FD3F749 /* RLMObjectBase.h in Copy . Public Headers */, - 25FAE49C26B45BE660E2BAAA1F3B8BB8 /* RLMObjectBase_Dynamic.h in Copy . Public Headers */, - CA5A604A65CCD04D66E35737677056DE /* RLMObjectSchema.h in Copy . Public Headers */, - 545176EB8BFA5C7897F7AA39BEC1C200 /* RLMOptionalBase.h in Copy . Public Headers */, - 526FEA939C967D5E183B06A931A0EAC0 /* RLMPlatform.h in Copy . Public Headers */, - 4439CC4507D923EF9BEC5F98FB2B432F /* RLMProperty.h in Copy . Public Headers */, - E3345ABB043E3BDC4612991B57FB3253 /* RLMRealm.h in Copy . Public Headers */, - D3970B0F6D5C4B87D8BF8D82B5730885 /* RLMRealm_Dynamic.h in Copy . Public Headers */, - DAB3C9C0EB19A56A34ABD018DBBD301A /* RLMRealmConfiguration+Sync.h in Copy . Public Headers */, - 1FB2164A2DD04DABA3E647DD8BA769A9 /* RLMRealmConfiguration.h in Copy . Public Headers */, - 2A08A909E281D40EAF14DF5EE0A3055B /* RLMResults.h in Copy . Public Headers */, - 4B049F5FF6C31394D8C106443419BA82 /* RLMSchema.h in Copy . Public Headers */, - F20893A324995F692FB8C2EBA32AAE8F /* RLMSyncConfiguration.h in Copy . Public Headers */, - EE7A2300183F8B7DD9A162D133B8032A /* RLMSyncCredentials.h in Copy . Public Headers */, - ECF00D7E9037203EC2BC5110B60F4B10 /* RLMSyncManager.h in Copy . Public Headers */, - D7B1B0A497CE8975B5B8187FBFF00798 /* RLMSyncPermissionChange.h in Copy . Public Headers */, - 06B76D890CEA891B7CC1502670DA4747 /* RLMSyncPermissionOffer.h in Copy . Public Headers */, - 356E31AEDC2125D57154B92860274112 /* RLMSyncPermissionOfferResponse.h in Copy . Public Headers */, - 21A9DEA3E42240805DF7D849B250F009 /* RLMSyncSession.h in Copy . Public Headers */, - 192BE4C9EFA0640AECD520063F1BC7C9 /* RLMSyncUser.h in Copy . Public Headers */, - 9E32655D8CA41C0FD3B1BE4829D11EF3 /* RLMSyncUtil.h in Copy . Public Headers */, - CB71100265DF9B80D65E60BBB9670353 /* RLMThreadSafeReference.h in Copy . Public Headers */, + 53160B4A55556EC639CF1E4ACE5FE784 /* RLMAccessor.h in Copy . Private Headers */, + 47BEC5DEDCA2C08F55E56EEA185641CD /* RLMArray_Private.h in Copy . Private Headers */, + 3B62FBF56D30A265E6B40FDD111696C5 /* RLMCollection_Private.h in Copy . Private Headers */, + 2931CE2E87E9B5B714C9B824226F07AD /* RLMListBase.h in Copy . Private Headers */, + 11AC774E046526553C87A3E70BFD02AF /* RLMMigration_Private.h in Copy . Private Headers */, + E21CB4DBF97C8B27D2C70B425C7A42EB /* RLMObject_Private.h in Copy . Private Headers */, + 608747D1FED76A804DD80BB89DD74122 /* RLMObjectBase_Private.h in Copy . Private Headers */, + B9E2ABEC49C60A27FB201EB15BEA4FE9 /* RLMObjectSchema_Private.h in Copy . Private Headers */, + 46AB04EB4D5583340A65BC43CBFB507A /* RLMObjectStore.h in Copy . Private Headers */, + B33EB368DBCFCCCA78BA43D770BCB626 /* RLMOptionalBase.h in Copy . Private Headers */, + 3BAB4C4AA768EC0419D5749CB82C75B4 /* RLMProperty_Private.h in Copy . Private Headers */, + 12144081E1F96FE5DDC9226FC87914B4 /* RLMRealm_Private.h in Copy . Private Headers */, + CF6CAA75B59F5CF308992849F688F6FE /* RLMRealmConfiguration_Private.h in Copy . Private Headers */, + 47010949994F76F8A18C8553448470DF /* RLMResults_Private.h in Copy . Private Headers */, + 65DBB00047899A34D9DF1DDFB389FF66 /* RLMSchema_Private.h in Copy . Private Headers */, + A5505D42DDC179396282B1CC880340DB /* RLMSyncConfiguration_Private.h in Copy . Private Headers */, + 024E96F046BEA94D3002FB34D0B684AC /* RLMSyncManager_Private.h in Copy . Private Headers */, + 669DF2CAAC0A642953E8577BDD08645B /* RLMSyncUtil_Private.h in Copy . Private Headers */, ); - name = "Copy . Public Headers"; + name = "Copy . Private Headers"; runOnlyForDeploymentPostprocessing = 0; }; - 28025138903B321FD11625053C9E0D56 /* Copy . Private Headers */ = { + C5B3D2AE4BCE07316F3C7364B175E4CD /* Copy . Public Headers */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; - dstPath = "$(PRIVATE_HEADERS_FOLDER_PATH)/."; + dstPath = "$(PUBLIC_HEADERS_FOLDER_PATH)/."; dstSubfolderSpec = 16; files = ( - 8BBE96797C2C3A52A6132A2FD8B4527B /* RLMAccessor.h in Copy . Private Headers */, - 94A050F1B8274A83A743CE3984A988D9 /* RLMArray_Private.h in Copy . Private Headers */, - 4E26E1B18FD78C4599EA573A9679489F /* RLMListBase.h in Copy . Private Headers */, - 4E82DE3BD3418A7E0F2BD2C9136B7334 /* RLMMigration_Private.h in Copy . Private Headers */, - C30D239388B285B78BB4ED6E99FC2751 /* RLMObject_Private.h in Copy . Private Headers */, - F9F10B7DAEE14E60B2F7C261C389DE63 /* RLMObjectSchema_Private.h in Copy . Private Headers */, - 7D6F4DBC7CFE7C6D11798E8F568A6F57 /* RLMObjectStore.h in Copy . Private Headers */, - C319C0C9044EB123E85259CD381E6321 /* RLMOptionalBase.h in Copy . Private Headers */, - 3A595525550DFDC17AA868B3367E0BD6 /* RLMProperty_Private.h in Copy . Private Headers */, - 2EA139B45B1CC7CBB99924EF793CDDEF /* RLMRealm_Private.h in Copy . Private Headers */, - 08450EB04D43DD17968CCBCFDD1CD605 /* RLMRealmConfiguration_Private.h in Copy . Private Headers */, - F0ED8E0D78E1B7374EFD4EC66EE9FAD2 /* RLMResults_Private.h in Copy . Private Headers */, - 7EDEC38A5EAD3F44F78F3399A5B0EBD4 /* RLMSchema_Private.h in Copy . Private Headers */, - 6298746B3A00D2EA171B3B964682FB49 /* RLMSyncConfiguration_Private.h in Copy . Private Headers */, - F09AA1B0410CA9380A78556A618FCBA1 /* RLMSyncManager_Private.h in Copy . Private Headers */, - 7DFD7F9215245EA744F91515EF68096A /* RLMSyncPermissionChange_Private.h in Copy . Private Headers */, - 3AC341F72F45E8CC0E585FE8C5ED7EE5 /* RLMSyncPermissionOffer_Private.h in Copy . Private Headers */, - E5810E41BDE5EFBA35111598FC81A410 /* RLMSyncPermissionOfferResponse_Private.h in Copy . Private Headers */, - 892D05EB11574FB6E74F81CF2482D97F /* RLMSyncUtil_Private.h in Copy . Private Headers */, + 25FB7C3D82CA6C541F1CE8BA892D1DD3 /* NSError+RLMSync.h in Copy . Public Headers */, + 267E463DE56F4458A269EB1D2AE79054 /* Realm.h in Copy . Public Headers */, + 46F860BC40EB0F3D5E45215C955207C5 /* RLMArray.h in Copy . Public Headers */, + AB20E8801433DF2706C58F984719275F /* RLMCollection.h in Copy . Public Headers */, + E648A4D608F1E8B6F6104DD673F37382 /* RLMConstants.h in Copy . Public Headers */, + 6B46F29203A7BED5054EDC9EC4BB1546 /* RLMListBase.h in Copy . Public Headers */, + AA86D52378C7D8A854461F1041F6F717 /* RLMMigration.h in Copy . Public Headers */, + 194D147D6A3C15FFB99900A925FC8B9A /* RLMObject.h in Copy . Public Headers */, + 0DE5E6F6D5A8C3F356F0087B7BA16191 /* RLMObjectBase.h in Copy . Public Headers */, + CFFEC41F2E81F8C8277AD6FD00B447E2 /* RLMObjectBase_Dynamic.h in Copy . Public Headers */, + 4AA00E8F043842A1974826BA525CFAAB /* RLMObjectSchema.h in Copy . Public Headers */, + B193E96033D067BA3D0133C9883A4EBC /* RLMOptionalBase.h in Copy . Public Headers */, + 587CF5E6828EAEFCB1677374EDEC4B72 /* RLMPlatform.h in Copy . Public Headers */, + 844ED9E2D730BD83D6BC206D336F3135 /* RLMProperty.h in Copy . Public Headers */, + 640270FF7B7CC7A4445CB23EBEEF1DEC /* RLMRealm+Sync.h in Copy . Public Headers */, + B467EC1A1428B231FDF2FFB190E2C6AC /* RLMRealm.h in Copy . Public Headers */, + 09A91871E469450301A2A804D0EBA656 /* RLMRealm_Dynamic.h in Copy . Public Headers */, + 6B160DB4D477853EB91FE5E2E4B36106 /* RLMRealmConfiguration+Sync.h in Copy . Public Headers */, + 23C4D0AB263D248C4468D7050DFC78E0 /* RLMRealmConfiguration.h in Copy . Public Headers */, + E5ED63D2E26F31E61195EBECC3CF9564 /* RLMResults.h in Copy . Public Headers */, + 8099CC4DA6F69D06755393EDB5BC9893 /* RLMSchema.h in Copy . Public Headers */, + 2E32FEE068EABDFBF2FEF0B20180BE88 /* RLMSyncConfiguration.h in Copy . Public Headers */, + 478EFB47E9C9D8C6E3580D4EF8C93DE6 /* RLMSyncCredentials.h in Copy . Public Headers */, + EE90DC769A183F60D6B815E3507EF8D9 /* RLMSyncManager.h in Copy . Public Headers */, + B113AA249649D0407F71C8360EB55084 /* RLMSyncPermission.h in Copy . Public Headers */, + 7E964866F1C3B481A83929A16D6AB3E4 /* RLMSyncSession.h in Copy . Public Headers */, + 99357F25FAAA58D881302ADAC7BB6F4B /* RLMSyncSubscription.h in Copy . Public Headers */, + 4CF76423EFD0B8B272AABDC7B39F8F5C /* RLMSyncUser.h in Copy . Public Headers */, + DAA7B5BCD613442490340F81AEDF3602 /* RLMSyncUtil.h in Copy . Public Headers */, + 04E4625EB2A26D8ED629FADF9E43843F /* RLMThreadSafeReference.h in Copy . Public Headers */, ); - name = "Copy . Private Headers"; + name = "Copy . Public Headers"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 00BD4125040D26E6F494F4A6BD624460 /* list_notifier.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = list_notifier.cpp; path = Realm/ObjectStore/src/impl/list_notifier.cpp; sourceTree = ""; }; - 0325E7C7F5AC1AE712D7975B1699FFE5 /* Realm.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Realm.swift; path = RealmSwift/Realm.swift; sourceTree = ""; }; - 03A6ADFB0A4503070DF6C5589F3F3E20 /* Section.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Section.swift; path = Source/Core/Section.swift; sourceTree = ""; }; - 03C6CE1F1C11DD39FEB2B8EF746CCC4E /* DateFieldRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DateFieldRow.swift; path = Source/Rows/Common/DateFieldRow.swift; sourceTree = ""; }; - 04F8D2F7C2B72C1FE0F3F9CFCD347B9C /* FieldsRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FieldsRow.swift; path = Source/Rows/FieldsRow.swift; sourceTree = ""; }; - 0628BD4AB36BFDD0F70F5E1DB1DBF67A /* NSError+RLMSync.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSError+RLMSync.m"; path = "Realm/NSError+RLMSync.m"; sourceTree = ""; }; - 0674E972D05DC625A513B8892778003F /* RLMSyncPermissionOfferResponse_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncPermissionOfferResponse_Private.h; path = include/RLMSyncPermissionOfferResponse_Private.h; sourceTree = ""; }; - 084D1115E70849A725D618FF67761945 /* Cell.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Cell.swift; path = Source/Core/Cell.swift; sourceTree = ""; }; - 08FA7D650AA736A88B61D2537CB3CD51 /* RLMObject_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObject_Private.h; path = include/RLMObject_Private.h; sourceTree = ""; }; - 0981BEFA01FEAE538C1510758D563982 /* RLMSyncSession.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncSession.h; path = include/RLMSyncSession.h; sourceTree = ""; }; - 0BD5E7FE8D39F82606529218C8B9EE24 /* RealmSwift-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "RealmSwift-umbrella.h"; sourceTree = ""; }; - 0D0589AF94C2F182098FF711C58BE901 /* PickerInlineRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PickerInlineRow.swift; path = Source/Rows/PickerInlineRow.swift; sourceTree = ""; }; - 0D950242A064EF6D387D762AC469B49A /* SegmentedRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SegmentedRow.swift; path = Source/Rows/SegmentedRow.swift; sourceTree = ""; }; - 0E9E7653EE6E11A9D714A5FBD6F5698E /* RLMArray_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMArray_Private.h; path = include/RLMArray_Private.h; sourceTree = ""; }; - 0F163D3E628B2C27B1508E055FCBB4FE /* RLMMigration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMMigration.h; path = include/RLMMigration.h; sourceTree = ""; }; - 0FA9915B3202603A2CEE9969409231EC /* RuleClosure.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RuleClosure.swift; path = Source/Validations/RuleClosure.swift; sourceTree = ""; }; - 10BBAB9E19BCC36E9E4EBF8BBF70EDEF /* RLMProperty.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMProperty.h; path = include/RLMProperty.h; sourceTree = ""; }; - 11964F2393D2D4030E37AF622F8F7DF8 /* RLMAuthResponseModel.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMAuthResponseModel.m; path = Realm/RLMAuthResponseModel.m; sourceTree = ""; }; - 11A77345025CB3D7B627A351D33D7F5A /* RLMRealmConfiguration+Sync.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "RLMRealmConfiguration+Sync.h"; path = "include/RLMRealmConfiguration+Sync.h"; sourceTree = ""; }; - 11BE0F9CF79C0D969D2CDF3F990A34DA /* RLMOptionalBase.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMOptionalBase.mm; path = Realm/RLMOptionalBase.mm; sourceTree = ""; }; - 12D42F13EF40C8807F1B51A7E183C2A6 /* RLMSyncUtil.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncUtil.mm; path = Realm/RLMSyncUtil.mm; sourceTree = ""; }; - 136A067D3B0AD5F09C9BD5EE82CAEBE8 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 13E8AC0A040A95EE5A90B6279F2FD01B /* collection_change_builder.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = collection_change_builder.cpp; path = Realm/ObjectStore/src/impl/collection_change_builder.cpp; sourceTree = ""; }; - 15DB1C10C2C45C61470E5FDAD410FADF /* realm_coordinator.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = realm_coordinator.cpp; path = Realm/ObjectStore/src/impl/realm_coordinator.cpp; sourceTree = ""; }; - 1729FA2C3E2386CDE39B9777073DCDC3 /* RLMRealm.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMRealm.h; path = include/RLMRealm.h; sourceTree = ""; }; - 17B7645C7D765D49975F13208959E78D /* FieldRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FieldRow.swift; path = Source/Rows/Common/FieldRow.swift; sourceTree = ""; }; - 18C1C43D04AF90570D89DF2922616B6E /* RLMResults.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMResults.h; path = include/RLMResults.h; sourceTree = ""; }; - 199A99A47685911D68DE2E3BFB8468EC /* RLMSyncSession.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncSession.mm; path = Realm/RLMSyncSession.mm; sourceTree = ""; }; - 1A18C31291781314DD00F7C5EB52F56F /* RLMObservation.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMObservation.mm; path = Realm/RLMObservation.mm; sourceTree = ""; }; - 1C3C9AC34A7A7C83578E7E6D2B8F0101 /* DecimalFormatter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DecimalFormatter.swift; path = Source/Rows/Common/DecimalFormatter.swift; sourceTree = ""; }; - 1CDBF32EDB2629D7DC97D7ED60FAFB32 /* sync_session.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = sync_session.cpp; path = Realm/ObjectStore/src/sync/sync_session.cpp; sourceTree = ""; }; - 1D3821CDA73B90CD3BD9B05955E5BC4C /* ObjectiveCSupport.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ObjectiveCSupport.swift; path = RealmSwift/ObjectiveCSupport.swift; sourceTree = ""; }; - 1D4BCE97A71DE1CDE56EEF209B6FE7CF /* shared_realm.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = shared_realm.cpp; path = Realm/ObjectStore/src/shared_realm.cpp; sourceTree = ""; }; - 1E13F8D90E94321D9AC567411EEAFEAB /* Eureka.bundle */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "wrapper.plug-in"; name = Eureka.bundle; path = Source/Resources/Eureka.bundle; sourceTree = ""; }; - 1E5E1F7504471A7DC6A681EC7E71A87D /* results.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = results.cpp; path = Realm/ObjectStore/src/results.cpp; sourceTree = ""; }; - 222C4BF8B3694CDFD93F89C630851F89 /* Pods-Anyway.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = "Pods-Anyway.modulemap"; sourceTree = ""; }; - 2295D12C85920E4B1E3F7706468DD794 /* RLMResults.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMResults.mm; path = Realm/RLMResults.mm; sourceTree = ""; }; - 240340D83B39E2F6B35CF6BC4CFD164C /* Property.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Property.swift; path = RealmSwift/Property.swift; sourceTree = ""; }; - 24CA718B9D06B3DE1F62BB0C374BE311 /* RLMRealmConfiguration_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMRealmConfiguration_Private.h; path = include/RLMRealmConfiguration_Private.h; sourceTree = ""; }; - 25E5133FA249E54193DCD306DB40A7A0 /* PopoverSelectorRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PopoverSelectorRow.swift; path = Source/Rows/PopoverSelectorRow.swift; sourceTree = ""; }; - 270E75F85B71197FEACBC19FBE748F37 /* RLMObjectBase.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMObjectBase.mm; path = Realm/RLMObjectBase.mm; sourceTree = ""; }; - 2719B5CBD57B584D6C0A839CFCC22254 /* sync_file.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = sync_file.cpp; path = Realm/ObjectStore/src/sync/impl/sync_file.cpp; sourceTree = ""; }; - 27C14377DF2A3FBB3817B4C9B050AB7F /* RLMAccessor.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMAccessor.h; path = include/RLMAccessor.h; sourceTree = ""; }; - 27D9D9A0B31ECE9B436F57B6B414091C /* Pods-Anyway-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Anyway-acknowledgements.plist"; sourceTree = ""; }; - 28375129CD5D31DE7F372C9F31C3D655 /* RealmSwift.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = RealmSwift.modulemap; sourceTree = ""; }; - 29D0734057FB701FB6B90FC6A17E9025 /* SelectorViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SelectorViewController.swift; path = Source/Rows/Controllers/SelectorViewController.swift; sourceTree = ""; }; - 2B46C6700BBE28BFE8DE7287BD5F560B /* Realm.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Realm.xcconfig; sourceTree = ""; }; + 001110A6560B90CAE1EB189F2495F8D1 /* RLMRealmConfiguration.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMRealmConfiguration.mm; path = Realm/RLMRealmConfiguration.mm; sourceTree = ""; }; + 00A48D5F9653E688D856A41C12A94455 /* DoublePickerInputRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DoublePickerInputRow.swift; path = Source/Rows/DoublePickerInputRow.swift; sourceTree = ""; }; + 032E3F03CAA28A8D926A754A61106180 /* placeholder.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = placeholder.cpp; path = Realm/ObjectStore/src/placeholder.cpp; sourceTree = ""; }; + 03330053F888BD923BF4C4CD4C46800C /* RLMSyncManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncManager.h; path = include/RLMSyncManager.h; sourceTree = ""; }; + 03AA6506C5ACCF14F540BDFAFF02204F /* Sync.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Sync.swift; path = RealmSwift/Sync.swift; sourceTree = ""; }; + 04002741C9DC9598E4522E5C7784BE74 /* RLMAnalytics.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMAnalytics.mm; path = Realm/RLMAnalytics.mm; sourceTree = ""; }; + 04102893CCCB7CD3F95033115D032B10 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 0447FED55AE17952ABB23A6B30DE55AB /* ThreadSafeReference.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ThreadSafeReference.swift; path = RealmSwift/ThreadSafeReference.swift; sourceTree = ""; }; + 0488E4C9B99E1C7153BD52CF62BE6FF5 /* Eureka-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Eureka-dummy.m"; sourceTree = ""; }; + 04E903ADF8F796BCAD79F12C0E094A5D /* RLMSyncPermissionResults.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncPermissionResults.mm; path = Realm/RLMSyncPermissionResults.mm; sourceTree = ""; }; + 0523C02E11706E1C18914D46C313EB27 /* Pods-Anyway-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Anyway-resources.sh"; sourceTree = ""; }; + 0947500D02EC4BF07CCC37BED5C229EF /* RowType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RowType.swift; path = Source/Core/RowType.swift; sourceTree = ""; }; + 097857138D9821D34C7FCB860A83C454 /* ServerTrustPolicy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ServerTrustPolicy.swift; path = Source/ServerTrustPolicy.swift; sourceTree = ""; }; + 0BEEC4B884A769315B12C502799ED7D9 /* RLMPredicateUtil.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMPredicateUtil.mm; path = Realm/RLMPredicateUtil.mm; sourceTree = ""; }; + 0EEAD778E47291DC5CA392CEAD168A93 /* List.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = List.swift; path = RealmSwift/List.swift; sourceTree = ""; }; + 0F620F20591BA62E035F169DE1CE3FE5 /* RLMMigration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMMigration.h; path = include/RLMMigration.h; sourceTree = ""; }; + 0F719281B4D3F905F5A67612C4BA14A2 /* Schema.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Schema.swift; path = RealmSwift/Schema.swift; sourceTree = ""; }; + 10450524A1F8D00FF064E5B7E2810EFD /* RLMSyncPermission.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncPermission.mm; path = Realm/RLMSyncPermission.mm; sourceTree = ""; }; + 10E3D436E5AC2DC7953C3BF2822B8F69 /* ObjectiveCSupport.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ObjectiveCSupport.swift; path = RealmSwift/ObjectiveCSupport.swift; sourceTree = ""; }; + 10EB8EE4D5BCC0493710CB96CC7CBA64 /* RLMSyncConfiguration.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncConfiguration.mm; path = Realm/RLMSyncConfiguration.mm; sourceTree = ""; }; + 113684CC953F62A508BFA81EC4AA2D22 /* RLMUtil.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMUtil.mm; path = Realm/RLMUtil.mm; sourceTree = ""; }; + 11CA426804A9BB072D81CAA11AD1F09A /* Realm-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Realm-dummy.m"; sourceTree = ""; }; + 11CD0690F393036A8C39A6F395DF660E /* Realm.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Realm.h; path = include/Realm.h; sourceTree = ""; }; + 1203D759E1683FCE548BB3DDD477B8BE /* RuleEmail.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RuleEmail.swift; path = Source/Validations/RuleEmail.swift; sourceTree = ""; }; + 124D4BADF34C3392E8B1E56EF4F2A663 /* index_set.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = index_set.cpp; path = Realm/ObjectStore/src/index_set.cpp; sourceTree = ""; }; + 126549DC194D6BB04E1151C548284B8D /* ButtonRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ButtonRow.swift; path = Source/Rows/ButtonRow.swift; sourceTree = ""; }; + 14C503A15D13A17BFC554427E7F9A28F /* RuleURL.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RuleURL.swift; path = Source/Validations/RuleURL.swift; sourceTree = ""; }; + 14D0A18795829341CE4B7A3C6A1EEEB0 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; + 14E99727ACE0BB6DB0B22DB41EE62C2F /* RLMSyncUser.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncUser.mm; path = Realm/RLMSyncUser.mm; sourceTree = ""; }; + 15557EEAE9332FBEF162AC42204AB072 /* RLMSchema.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSchema.h; path = include/RLMSchema.h; sourceTree = ""; }; + 175A9E8928769E05D3AD017C36A4F8E2 /* RLMSyncSessionRefreshHandle.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncSessionRefreshHandle.mm; path = Realm/RLMSyncSessionRefreshHandle.mm; sourceTree = ""; }; + 176E8EC2BE2AEC13B33AED131EFC8FC1 /* RealmSwift-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "RealmSwift-umbrella.h"; sourceTree = ""; }; + 187B02672F278FDAB7F49040F27FF40D /* AlertOptionsRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AlertOptionsRow.swift; path = Source/Rows/Common/AlertOptionsRow.swift; sourceTree = ""; }; + 1898E6460BE4062611AF6E8820C8E0BA /* LinkingObjects.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LinkingObjects.swift; path = RealmSwift/LinkingObjects.swift; sourceTree = ""; }; + 18EA699644630EFE9C3F737E1D7AA22A /* RLMCollection.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMCollection.h; path = include/RLMCollection.h; sourceTree = ""; }; + 194A2BE505076878605C44E278887A41 /* list_notifier.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = list_notifier.cpp; path = Realm/ObjectStore/src/impl/list_notifier.cpp; sourceTree = ""; }; + 1B0270EC4A80A78F37460EDA7A9EB51E /* collection_notifications.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = collection_notifications.cpp; path = Realm/ObjectStore/src/collection_notifications.cpp; sourceTree = ""; }; + 1B57A881AACF13C810FE8799270BC239 /* RLMSyncManager_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncManager_Private.h; path = include/RLMSyncManager_Private.h; sourceTree = ""; }; + 1BE4DDB16CBE64F4A80116FA616B5E61 /* RLMObject_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObject_Private.h; path = include/RLMObject_Private.h; sourceTree = ""; }; + 1D700D1CF120FEA45E20E220BBD986A1 /* RLMSyncConfiguration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncConfiguration.h; path = include/RLMSyncConfiguration.h; sourceTree = ""; }; + 1DE5C10D442C8A56503D53FC9EFE1384 /* Row.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Row.swift; path = Source/Core/Row.swift; sourceTree = ""; }; + 1E205473F66611EB373B5AD7DF083FDE /* RuleEqualsToRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RuleEqualsToRow.swift; path = Source/Validations/RuleEqualsToRow.swift; sourceTree = ""; }; + 20975C0C353901A50CCED023965AF70A /* SelectorRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SelectorRow.swift; path = Source/Rows/Common/SelectorRow.swift; sourceTree = ""; }; + 246EB3837726573E0FFCC0A9481009D3 /* Realm.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Realm.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 24E2229A2BA4D575C6E5A758F693A40F /* object_notifier.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = object_notifier.cpp; path = Realm/ObjectStore/src/impl/object_notifier.cpp; sourceTree = ""; }; + 25456002CE8D3E5F3359AF03BEA449D4 /* RLMObservation.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMObservation.mm; path = Realm/RLMObservation.mm; sourceTree = ""; }; + 25B113DF62FF81079C8BFDA88738D5B5 /* DoublePickerRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DoublePickerRow.swift; path = Source/Rows/DoublePickerRow.swift; sourceTree = ""; }; + 26E6FB2127BE3D720B17AFD1B323E0C0 /* RuleRequired.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RuleRequired.swift; path = Source/Validations/RuleRequired.swift; sourceTree = ""; }; + 2819BE1610A8748120C720DC3AA0C66B /* FieldRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FieldRow.swift; path = Source/Rows/Common/FieldRow.swift; sourceTree = ""; }; + 28210C7A86CBE8162C3D55C22079D4B2 /* RLMRealm_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMRealm_Private.h; path = include/RLMRealm_Private.h; sourceTree = ""; }; + 28F0C763A6216AC9BEF1488A3FF128B6 /* Object.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Object.swift; path = RealmSwift/Object.swift; sourceTree = ""; }; + 296CB55358BAA7E6D7F3ED0162B610EF /* Notifications.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Notifications.swift; path = Source/Notifications.swift; sourceTree = ""; }; + 2AB6D864DBC97BBB5C7C05F5540E404B /* Timeline.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Timeline.swift; path = Source/Timeline.swift; sourceTree = ""; }; + 2C0ED9FAB960CDD538A5C953B92F74C8 /* Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Alamofire.swift; path = Source/Alamofire.swift; sourceTree = ""; }; 2C14F37B8979FA208AAB1E084A4AC5A7 /* Eureka.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Eureka.framework; path = Eureka.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 2CC97A6737F1714F9EF84DDE43475C07 /* list.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = list.cpp; path = Realm/ObjectStore/src/list.cpp; sourceTree = ""; }; - 2D0CE63E3233460D4B17968656664499 /* PickerRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PickerRow.swift; path = Source/Rows/PickerRow.swift; sourceTree = ""; }; - 2D403206900FBDE3E3E8F9E470DC1D7C /* ActionSheetRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ActionSheetRow.swift; path = Source/Rows/ActionSheetRow.swift; sourceTree = ""; }; - 2E3B856EDE75C56D20E19B60DA7C900D /* RLMSyncPermissionChange_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncPermissionChange_Private.h; path = include/RLMSyncPermissionChange_Private.h; sourceTree = ""; }; - 2EFD0126BC7BBC68CDA40D17FF5FF701 /* LabelRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LabelRow.swift; path = Source/Rows/LabelRow.swift; sourceTree = ""; }; - 2F550759D3307B38E90A1D9799D000DF /* RLMNetworkClient.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMNetworkClient.m; path = Realm/RLMNetworkClient.m; sourceTree = ""; }; - 32D27D06530CC7C698C09158E97B1714 /* RLMObjectBase_Dynamic.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObjectBase_Dynamic.h; path = include/RLMObjectBase_Dynamic.h; sourceTree = ""; }; - 3431B2BF741F061F001CE09129307498 /* RLMSyncSessionRefreshHandle.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncSessionRefreshHandle.mm; path = Realm/RLMSyncSessionRefreshHandle.mm; sourceTree = ""; }; - 36B2E6217FCAFD472E13DA1BFB06DD25 /* MultipleSelectorViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MultipleSelectorViewController.swift; path = Source/Rows/Controllers/MultipleSelectorViewController.swift; sourceTree = ""; }; - 373AB91A812DCAD2C3036DA8E3DEDFB6 /* RuleEqualsToRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RuleEqualsToRow.swift; path = Source/Validations/RuleEqualsToRow.swift; sourceTree = ""; }; - 3849234A5CFEC796842D8A0FF2EEAAE4 /* RLMUtil.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMUtil.mm; path = Realm/RLMUtil.mm; sourceTree = ""; }; - 386DCD377B9E54E299E4F8CEC8FF48E3 /* MultipartFormData.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MultipartFormData.swift; path = Source/MultipartFormData.swift; sourceTree = ""; }; - 38AA4F2C6E7EAC364B55224D98763F0B /* RLMThreadSafeReference.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMThreadSafeReference.h; path = include/RLMThreadSafeReference.h; sourceTree = ""; }; - 393D848359E7D9EA8202A481BE76DB05 /* placeholder.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = placeholder.cpp; path = Realm/ObjectStore/src/placeholder.cpp; sourceTree = ""; }; - 3C59F0E01E7F300BE1CFDDECCB8180EF /* RowProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RowProtocols.swift; path = Source/Core/RowProtocols.swift; sourceTree = ""; }; - 3E231FEB8719347E19C2093B5BB82875 /* SortDescriptor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SortDescriptor.swift; path = RealmSwift/SortDescriptor.swift; sourceTree = ""; }; - 3E454138880D8212824C37378378EEF4 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 3FFBCFC6554676810CEBD0FB23414331 /* format.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = format.cpp; path = Realm/ObjectStore/src/util/format.cpp; sourceTree = ""; }; - 407F9684B0240170F95F9A6F7E844E68 /* Result.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Result.swift; path = Source/Result.swift; sourceTree = ""; }; - 4138285A9AFDCD8EA8A861545BAED78F /* RLMRealm_Dynamic.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMRealm_Dynamic.h; path = include/RLMRealm_Dynamic.h; sourceTree = ""; }; - 43AB36B77AF38537BD827526EF2B7E97 /* RLMProperty.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMProperty.mm; path = Realm/RLMProperty.mm; sourceTree = ""; }; - 449CF09F2B27B4F97F08D2453AAC4DA6 /* SelectorRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SelectorRow.swift; path = Source/Rows/Common/SelectorRow.swift; sourceTree = ""; }; - 44FF55BF977A4603AABEB5D2C38C1BE7 /* librealm-ios.a */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = archive.ar; name = "librealm-ios.a"; path = "core/librealm-ios.a"; sourceTree = ""; }; - 4527BBFB0222D834AEC2504878D0E941 /* RLMSyncConfiguration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncConfiguration.h; path = include/RLMSyncConfiguration.h; sourceTree = ""; }; - 467FB620E0DFEF99D2F491E6D28CD4E5 /* RLMObjectSchema.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMObjectSchema.mm; path = Realm/RLMObjectSchema.mm; sourceTree = ""; }; - 46E6854A887E54294337DE1E1DC716B1 /* Pods-Anyway-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-Anyway-acknowledgements.markdown"; sourceTree = ""; }; - 47F175DC5CF5301F4BDBC428D5DF65BC /* Realm.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = Realm.modulemap; sourceTree = ""; }; - 4864FA14696A7225D14E5BAC8F7F448D /* RLMResults_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMResults_Private.h; path = include/RLMResults_Private.h; sourceTree = ""; }; - 48C69975462872C6FA68F38E77619C9C /* Eureka.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = Eureka.modulemap; sourceTree = ""; }; + 2CACB94AD762BB71C001B2762B43E660 /* RLMThreadSafeReference.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMThreadSafeReference.mm; path = Realm/RLMThreadSafeReference.mm; sourceTree = ""; }; + 2D2FC371C278EA6E7577D95C098247BC /* StepperRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = StepperRow.swift; path = Source/Rows/StepperRow.swift; sourceTree = ""; }; + 2E6B269162560363B382DF5BC7EF1C10 /* RuleRange.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RuleRange.swift; path = Source/Validations/RuleRange.swift; sourceTree = ""; }; + 2E6D45894756DCCFAF546F9C2EC4CC1A /* RLMSyncUtil_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncUtil_Private.h; path = include/RLMSyncUtil_Private.h; sourceTree = ""; }; + 2F6043E48DDC0ABA3B90D64A62B848BB /* SelectorAlertController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SelectorAlertController.swift; path = Source/Rows/Controllers/SelectorAlertController.swift; sourceTree = ""; }; + 30AE50683B795C951A63D332AD22223C /* SwiftyJSON-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SwiftyJSON-dummy.m"; sourceTree = ""; }; + 31A4A4B5B08D1395E2F90F18F0F1DD24 /* RealmSwift-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "RealmSwift-prefix.pch"; sourceTree = ""; }; + 31FD96ACD62FF946B63DD498461E60A2 /* CellType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CellType.swift; path = Source/Core/CellType.swift; sourceTree = ""; }; + 327C13A5E90EFC1A5BF2C497A34D014A /* partial_sync.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = partial_sync.cpp; path = Realm/ObjectStore/src/sync/partial_sync.cpp; sourceTree = ""; }; + 3318D76ECFB236EFC0BE76A4FB42F14C /* NSError+RLMSync.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSError+RLMSync.m"; path = "Realm/NSError+RLMSync.m"; sourceTree = ""; }; + 344A2CB7F04E410D131FD5DFDC3E5C40 /* RLMQueryUtil.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMQueryUtil.mm; path = Realm/RLMQueryUtil.mm; sourceTree = ""; }; + 370D52E54F48A531CF8544EAD9E80C15 /* Realm.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Realm.xcconfig; sourceTree = ""; }; + 3A3E10A84830BB961553A10D85557C35 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; + 3D114CB4B4F86FEAD7367E0C96B5C20E /* RealmSwift-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "RealmSwift-dummy.m"; sourceTree = ""; }; + 3D661C888B638570C004F215A0184808 /* Form.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Form.swift; path = Source/Core/Form.swift; sourceTree = ""; }; + 3E40088A1D0BC7B2FEE5F5BEA221FF6F /* RLMResults.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMResults.mm; path = Realm/RLMResults.mm; sourceTree = ""; }; + 40586DDC9DF702A45F5EBEC431419383 /* results.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = results.cpp; path = Realm/ObjectStore/src/results.cpp; sourceTree = ""; }; + 40DC828D14E27D6671A3F0DF8EFA3FEC /* PickerInputRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PickerInputRow.swift; path = Source/Rows/PickerInputRow.swift; sourceTree = ""; }; + 4214080B2BE7243648C4456549CE8182 /* PopoverSelectorRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PopoverSelectorRow.swift; path = Source/Rows/PopoverSelectorRow.swift; sourceTree = ""; }; + 43198200D81D6B690D170883B3D376ED /* schema.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = schema.cpp; path = Realm/ObjectStore/src/schema.cpp; sourceTree = ""; }; + 43B756E9365921724D5897717226AE33 /* Alamofire.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Alamofire.xcconfig; sourceTree = ""; }; + 43F37F19567EFD7F2634E392C4938D43 /* DateInlineFieldRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DateInlineFieldRow.swift; path = Source/Rows/Common/DateInlineFieldRow.swift; sourceTree = ""; }; + 45816D1BAA763492080FC611A65F04C9 /* Protocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Protocols.swift; path = Source/Rows/Common/Protocols.swift; sourceTree = ""; }; + 45AB90ECD8AB5DD612FB49FDD020BAEE /* Alamofire-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Alamofire-dummy.m"; sourceTree = ""; }; + 46420844EAA978DC4528A13BB970D63B /* RLMSyncSession.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncSession.h; path = include/RLMSyncSession.h; sourceTree = ""; }; + 4674073253B272A5C44AD0FE471E57A8 /* SwiftyJSON.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = SwiftyJSON.modulemap; sourceTree = ""; }; + 477D374261C345967081120A29C9D12C /* RLMArray.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMArray.h; path = include/RLMArray.h; sourceTree = ""; }; + 49225B20499ADADDFE4F21C944855FA6 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 49A2B3436DB98A5B3255CDD8ABC660FC /* weak_realm_notifier.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = weak_realm_notifier.cpp; path = Realm/ObjectStore/src/impl/weak_realm_notifier.cpp; sourceTree = ""; }; + 49AB68E08DFCC8BB5588683D51F70C42 /* sync_file.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = sync_file.cpp; path = Realm/ObjectStore/src/sync/impl/sync_file.cpp; sourceTree = ""; }; + 4A7F0A0B8DFBE77352F497BE3B698066 /* Validation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Validation.swift; path = Source/Core/Validation.swift; sourceTree = ""; }; 4BD6512561193D405BA764A6D9A44D9C /* Pods_Anyway.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_Anyway.framework; path = "Pods-Anyway.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; - 4C0039B9DDC2472BE35918784A90AA0B /* Sync.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Sync.swift; path = RealmSwift/Sync.swift; sourceTree = ""; }; - 4C65356B61466B850C0C42E1D84A3104 /* CellType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CellType.swift; path = Source/Core/CellType.swift; sourceTree = ""; }; - 4C7C80E15B5DE9055E1FB944495DC8BB /* external_commit_helper.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = external_commit_helper.cpp; path = Realm/ObjectStore/src/impl/apple/external_commit_helper.cpp; sourceTree = ""; }; - 4E4498D0A2C0E7E34E8068D4254AECF6 /* weak_realm_notifier.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = weak_realm_notifier.cpp; path = Realm/ObjectStore/src/impl/weak_realm_notifier.cpp; sourceTree = ""; }; + 4BFE492D70CF60F74A461A4C34BA70F0 /* RLMRealm+Sync.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "RLMRealm+Sync.mm"; path = "Realm/RLMRealm+Sync.mm"; sourceTree = ""; }; + 4CA9E23211534D85FD7AC2B114442295 /* Pods-Anyway-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-Anyway-dummy.m"; sourceTree = ""; }; + 4D7E4F5987926F2C2BC25B00EA185D7A /* RLMSyncUtil.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncUtil.h; path = include/RLMSyncUtil.h; sourceTree = ""; }; + 4DD427720AB8D7C10B9C2F9F6B691617 /* Eureka.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Eureka.xcconfig; sourceTree = ""; }; + 4E187DCAE0A137EF50EB8FDC44B52168 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 4F233308B016878951FBBDDB04852B71 /* Alamofire.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Alamofire.framework; path = Alamofire.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 502995188A080E6D6436BF64DAC9E5B4 /* RLMSyncUser.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncUser.h; path = include/RLMSyncUser.h; sourceTree = ""; }; - 505EB98642A13AD8D2015D0E58D9181F /* RLMSyncManager_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncManager_Private.h; path = include/RLMSyncManager_Private.h; sourceTree = ""; }; - 5064684CC6FE7B593161C2C62A7BDFB1 /* DispatchQueue+Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "DispatchQueue+Alamofire.swift"; path = "Source/DispatchQueue+Alamofire.swift"; sourceTree = ""; }; - 508004E59A84C9E2BD7545539AB2B89A /* RLMArrayLinkView.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMArrayLinkView.mm; path = Realm/RLMArrayLinkView.mm; sourceTree = ""; }; - 50FB6F40AB6BB889316E02C3F71DFFB1 /* SliderRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SliderRow.swift; path = Source/Rows/SliderRow.swift; sourceTree = ""; }; - 520F99B6CFE5ED981E6BAC5559A35473 /* RowType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RowType.swift; path = Source/Core/RowType.swift; sourceTree = ""; }; - 531746272E0DBC0075555120A675147D /* InlineRowType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = InlineRowType.swift; path = Source/Core/InlineRowType.swift; sourceTree = ""; }; - 5424BE0CC5F454C6457E5F589E917AEC /* PushRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PushRow.swift; path = Source/Rows/PushRow.swift; sourceTree = ""; }; - 552761D4035022EF8D5177C8CBF20E74 /* Realm-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Realm-prefix.pch"; sourceTree = ""; }; - 5783296C13B6AB8D723ED137E1D96506 /* RLMArray.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMArray.h; path = include/RLMArray.h; sourceTree = ""; }; - 57B66886E0B5A2FF762539B9888E7D31 /* RLMCollection.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMCollection.h; path = include/RLMCollection.h; sourceTree = ""; }; - 5984CBF88D06136A0A23DE62752A4CB7 /* Alamofire-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Alamofire-dummy.m"; sourceTree = ""; }; - 59BF4F23201A68D17489D807C9E924B3 /* Timeline.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Timeline.swift; path = Source/Timeline.swift; sourceTree = ""; }; - 5A0DD02D17A83F4D2E50F8FCFE01B983 /* RLMSwiftSupport.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMSwiftSupport.m; path = Realm/RLMSwiftSupport.m; sourceTree = ""; }; - 5A974C2BACAA1BC8BD9AB9085A77C421 /* Pods-Anyway.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Anyway.debug.xcconfig"; sourceTree = ""; }; - 5B625B7E1AE15C871934DC51A58AB73E /* ServerTrustPolicy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ServerTrustPolicy.swift; path = Source/ServerTrustPolicy.swift; sourceTree = ""; }; - 5BEC800BBADAA10F12A2907AD52489C8 /* schema.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = schema.cpp; path = Realm/ObjectStore/src/schema.cpp; sourceTree = ""; }; - 5C41146D58D9108139484E4619940C2E /* object.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = object.cpp; path = Realm/ObjectStore/src/object.cpp; sourceTree = ""; }; - 5FEC3405757873883235969FC527F2CE /* NetworkReachabilityManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NetworkReachabilityManager.swift; path = Source/NetworkReachabilityManager.swift; sourceTree = ""; }; - 6039B922F638860B9FC3874C915E0671 /* RLMSyncCredentials.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncCredentials.h; path = include/RLMSyncCredentials.h; sourceTree = ""; }; - 60642B9E5F01ED218D76A28301764A6F /* RLMRealm_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMRealm_Private.h; path = include/RLMRealm_Private.h; sourceTree = ""; }; - 619BF1FD92B3D6FE97860B2CBF9229C2 /* index_set.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = index_set.cpp; path = Realm/ObjectStore/src/index_set.cpp; sourceTree = ""; }; - 62F10F059C31B04CCD3DBB715790FADC /* RLMSchema.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSchema.mm; path = Realm/RLMSchema.mm; sourceTree = ""; }; - 64B6D07C5164ED664F551EBD8B081CE7 /* RLMRealmConfiguration.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMRealmConfiguration.mm; path = Realm/RLMRealmConfiguration.mm; sourceTree = ""; }; - 6644A671463D040EAD5387939EB43DA6 /* DateInlineFieldRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DateInlineFieldRow.swift; path = Source/Rows/Common/DateInlineFieldRow.swift; sourceTree = ""; }; - 66460A48ED0E8EB446519BCC5A09057E /* Eureka-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Eureka-umbrella.h"; sourceTree = ""; }; - 686701257DF37DBC70A18137BAD60B7D /* RLMAnalytics.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMAnalytics.mm; path = Realm/RLMAnalytics.mm; sourceTree = ""; }; - 686F5B1BAF09AE338CA46A328DE359FB /* Validation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Validation.swift; path = Source/Validation.swift; sourceTree = ""; }; - 68E9156099F1EB7EDE460D4E157BC6B4 /* SwiftyJSON.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = SwiftyJSON.modulemap; sourceTree = ""; }; - 6952E45E49E7F662E8E8F9B51CA18863 /* Response.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Response.swift; path = Source/Response.swift; sourceTree = ""; }; - 6A284B16D14F93475C41DAE1873649B5 /* PresenterRowType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PresenterRowType.swift; path = Source/Core/PresenterRowType.swift; sourceTree = ""; }; - 6B0A8743C5F9B5685DF0FF5B8D162D8D /* AlertRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AlertRow.swift; path = Source/Rows/AlertRow.swift; sourceTree = ""; }; - 6C05CDAFA31C3E20FFA2BA6425E1CEBF /* RLMSyncUtil_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncUtil_Private.h; path = include/RLMSyncUtil_Private.h; sourceTree = ""; }; - 6C553278AD96C3EB670F12331FDD74B6 /* Eureka-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Eureka-prefix.pch"; sourceTree = ""; }; - 6D9BCF83EC8706B0D38C3F5CA6912FF3 /* RuleURL.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RuleURL.swift; path = Source/Validations/RuleURL.swift; sourceTree = ""; }; - 6F7E0ADF17EBC958B251815CC7568CDA /* Core.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Core.swift; path = Source/Core/Core.swift; sourceTree = ""; }; - 713BB555A898D021D9A96132F303A3D5 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 7216CA306EE64A30BA3F7624E146E93D /* RLMObjectStore.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObjectStore.h; path = include/RLMObjectStore.h; sourceTree = ""; }; - 72C33C05F0CC7EEDF1F38905DDE92572 /* Object.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Object.swift; path = RealmSwift/Object.swift; sourceTree = ""; }; - 7311A2BBD138BA02D3D1AFEB8009F42F /* SessionDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SessionDelegate.swift; path = Source/SessionDelegate.swift; sourceTree = ""; }; - 75DF55B3A46FC49075AE4BD6C9EA383E /* RLMMigration_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMMigration_Private.h; path = include/RLMMigration_Private.h; sourceTree = ""; }; - 773791FAABE10D0CD841A405B8EBD833 /* Helpers.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Helpers.swift; path = Source/Core/Helpers.swift; sourceTree = ""; }; - 7A3D9DA2C71CA686A1C42E7E25069931 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; - 7BFBD5CE655281E8C717BADD52E4958A /* RLMSyncPermissionChange.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMSyncPermissionChange.m; path = Realm/RLMSyncPermissionChange.m; sourceTree = ""; }; - 7C82B5E5218853D5FF1D99A144639C0C /* Optional.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Optional.swift; path = RealmSwift/Optional.swift; sourceTree = ""; }; - 7CED07EB9BDAFA7879F0A30A5824B79D /* Util.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Util.swift; path = RealmSwift/Util.swift; sourceTree = ""; }; - 7DCBEA864CF823C46E898153B245D8E5 /* ThreadSafeReference.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ThreadSafeReference.swift; path = RealmSwift/ThreadSafeReference.swift; sourceTree = ""; }; - 7EAA673441B602B9A00FF426FBD36DA0 /* RLMObjectBase.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObjectBase.h; path = include/RLMObjectBase.h; sourceTree = ""; }; - 7FEF75D28C51DD2C5CB7D482BBC916EA /* RLMSyncConfiguration.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncConfiguration.mm; path = Realm/RLMSyncConfiguration.mm; sourceTree = ""; }; - 8241C2625DF4891B7FC2A4445198A6CB /* NSError+RLMSync.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSError+RLMSync.h"; path = "include/NSError+RLMSync.h"; sourceTree = ""; }; - 834DAC5B97D31C4295577526C26859DD /* RLMSyncManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncManager.h; path = include/RLMSyncManager.h; sourceTree = ""; }; - 842AFED9641BA042831DB7383E12A2A0 /* results_notifier.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = results_notifier.cpp; path = Realm/ObjectStore/src/impl/results_notifier.cpp; sourceTree = ""; }; - 8436E1EDA6FAD0175D5D6728FA4DF0CA /* SwiftyJSON.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftyJSON.xcconfig; sourceTree = ""; }; - 8441212AE3E623321A15CF9A2931A5E4 /* RLMRealmConfiguration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMRealmConfiguration.h; path = include/RLMRealmConfiguration.h; sourceTree = ""; }; - 84471FEA32F71EB36161B5F2954E43B0 /* RLMPredicateUtil.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMPredicateUtil.mm; path = Realm/RLMPredicateUtil.mm; sourceTree = ""; }; - 846820F180FF593D8B30F33F453BF0B8 /* RLMArray.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMArray.mm; path = Realm/RLMArray.mm; sourceTree = ""; }; - 84DD011B908DA95F09D9D14D38BA0249 /* ParameterEncoding.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ParameterEncoding.swift; path = Source/ParameterEncoding.swift; sourceTree = ""; }; - 86382517D56D223CE543C3F1067B3358 /* Eureka.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Eureka.xcconfig; sourceTree = ""; }; - 86627A77089E844EBACD47C208071806 /* ObjectSchema.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ObjectSchema.swift; path = RealmSwift/ObjectSchema.swift; sourceTree = ""; }; - 882F164A5D289B372807FDDBD1A1CB62 /* RLMRealmConfiguration+Sync.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "RLMRealmConfiguration+Sync.mm"; path = "Realm/RLMRealmConfiguration+Sync.mm"; sourceTree = ""; }; - 883CEA612E14A5F05417D6AD6B3297BF /* CheckRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CheckRow.swift; path = Source/Rows/CheckRow.swift; sourceTree = ""; }; - 894692C9AB9605E81E9C6D693B2CDEF7 /* object_notifier.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = object_notifier.cpp; path = Realm/ObjectStore/src/impl/object_notifier.cpp; sourceTree = ""; }; - 8991107A2B7B547BA854EAB256F08781 /* StepperRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = StepperRow.swift; path = Source/Rows/StepperRow.swift; sourceTree = ""; }; - 8D4A407D80D0F5AD849A44674551336F /* Realm.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Realm.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 8D89CAA2E126C17A26758614230011E6 /* Results.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Results.swift; path = RealmSwift/Results.swift; sourceTree = ""; }; - 8E513C73F59953C62AFE3B41B410F108 /* RLMSyncPermissionOffer_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncPermissionOffer_Private.h; path = include/RLMSyncPermissionOffer_Private.h; sourceTree = ""; }; - 8FC1C64BD2D33913F8DDD197764A345E /* SwiftyJSON-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftyJSON-umbrella.h"; sourceTree = ""; }; - 90123BC19B6698C2EA04510241E8A9F1 /* NavigationAccessoryView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NavigationAccessoryView.swift; path = Source/Core/NavigationAccessoryView.swift; sourceTree = ""; }; - 90AF4D3347901168470C6AE662FC9D13 /* RealmSwift-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "RealmSwift-prefix.pch"; sourceTree = ""; }; - 920C51B4A494C88164B00C22CD447B4B /* Migration.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Migration.swift; path = RealmSwift/Migration.swift; sourceTree = ""; }; - 92549DA3A81A89CD2DCD9EDF70CE9D85 /* RLMSyncPermissionOffer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMSyncPermissionOffer.m; path = Realm/RLMSyncPermissionOffer.m; sourceTree = ""; }; + 5148E4A64F089541BBC2DDEDFC60CBD7 /* RealmConfiguration.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RealmConfiguration.swift; path = RealmSwift/RealmConfiguration.swift; sourceTree = ""; }; + 51D319F3644A3B5368DF418F82C60851 /* PickerInlineRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PickerInlineRow.swift; path = Source/Rows/PickerInlineRow.swift; sourceTree = ""; }; + 51DD1B1FA32059B7169BC128A060A5BC /* TextAreaRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TextAreaRow.swift; path = Source/Rows/TextAreaRow.swift; sourceTree = ""; }; + 523DBDBC9549556983C47F1BC098034B /* Helpers.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Helpers.swift; path = Source/Core/Helpers.swift; sourceTree = ""; }; + 529D2CCFB71D0697A0F9F093C4D6FAEA /* results_notifier.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = results_notifier.cpp; path = Realm/ObjectStore/src/impl/results_notifier.cpp; sourceTree = ""; }; + 53892895AD736DB83F24CB66C8BD5742 /* SessionDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SessionDelegate.swift; path = Source/SessionDelegate.swift; sourceTree = ""; }; + 55883507AB23386BC49FDAA28C3C368A /* RLMSyncPermission.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncPermission.h; path = include/RLMSyncPermission.h; sourceTree = ""; }; + 558D13F22302404CE3C9B913877AE37A /* RLMRealmConfiguration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMRealmConfiguration.h; path = include/RLMRealmConfiguration.h; sourceTree = ""; }; + 55BF6C9C3259F218D33485F16A52709C /* SliderRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SliderRow.swift; path = Source/Rows/SliderRow.swift; sourceTree = ""; }; + 57C9F248C0AEC8E0A677F1A588EA291D /* RLMSchema_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSchema_Private.h; path = include/RLMSchema_Private.h; sourceTree = ""; }; + 586218F9863E99A2078D4466D65A01D2 /* network_reachability_observer.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = network_reachability_observer.cpp; path = Realm/ObjectStore/src/sync/impl/apple/network_reachability_observer.cpp; sourceTree = ""; }; + 588E52B881F749AF8A57755A955AE4FC /* RLMSyncManager.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncManager.mm; path = Realm/RLMSyncManager.mm; sourceTree = ""; }; + 590D565903C5870CE8E04F9714DCADAF /* RLMSyncCredentials.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMSyncCredentials.m; path = Realm/RLMSyncCredentials.m; sourceTree = ""; }; + 59EE81AEC41C45CC0D5D8D10B5E2FE14 /* Cell.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Cell.swift; path = Source/Core/Cell.swift; sourceTree = ""; }; + 5A42EC471968E262A3E4B2D87D97EABC /* Response.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Response.swift; path = Source/Response.swift; sourceTree = ""; }; + 5D1D46BBFB6D9B74339D10D0FC54F60F /* RLMSyncSubscription.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncSubscription.h; path = include/RLMSyncSubscription.h; sourceTree = ""; }; + 5D58DDDAF8A6088DF40E48B6DB866494 /* RLMListBase.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMListBase.mm; path = Realm/RLMListBase.mm; sourceTree = ""; }; + 5DF7BA853685394814667C72C4229105 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 5E81A5B9FF8E4836044E1AB76D5B216C /* DateFieldRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DateFieldRow.swift; path = Source/Rows/Common/DateFieldRow.swift; sourceTree = ""; }; + 6024BE960393D401008CED073BF041D5 /* list.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = list.cpp; path = Realm/ObjectStore/src/list.cpp; sourceTree = ""; }; + 61DBF1126B97DC2DC3232D511E978638 /* Realm.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = Realm.modulemap; sourceTree = ""; }; + 62C933F380DD2F897B5294CD6BC830C9 /* Migration.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Migration.swift; path = RealmSwift/Migration.swift; sourceTree = ""; }; + 649F42D8A431E971B7E9584E04BC442C /* RLMObjectBase.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMObjectBase.mm; path = Realm/RLMObjectBase.mm; sourceTree = ""; }; + 64CE84F06C000C1E7D1BAAE4FA21C03E /* TriplePickerRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TriplePickerRow.swift; path = Source/Rows/TriplePickerRow.swift; sourceTree = ""; }; + 64D8D0B21B9ACFD54B1A199C25EB73AB /* RLMAccessor.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMAccessor.h; path = include/RLMAccessor.h; sourceTree = ""; }; + 655EFBA9BCFB51B0DAEC75CC202C03CD /* uuid.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = uuid.cpp; path = Realm/ObjectStore/src/util/uuid.cpp; sourceTree = ""; }; + 6649B98D9AD056DE4CFFA3D8A37F8925 /* RLMManagedArray.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMManagedArray.mm; path = Realm/RLMManagedArray.mm; sourceTree = ""; }; + 6C8B8BB4B7023504359EB1746965A799 /* ObjectSchema.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ObjectSchema.swift; path = RealmSwift/ObjectSchema.swift; sourceTree = ""; }; + 6C8D84EC7A695F0DFDA1DD6F69A82FE0 /* collection_change_builder.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = collection_change_builder.cpp; path = Realm/ObjectStore/src/impl/collection_change_builder.cpp; sourceTree = ""; }; + 6D9A0771F7C185DD785291A0CF832D3A /* RealmSwift.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = RealmSwift.modulemap; sourceTree = ""; }; + 6DA2151BB06A37D425D00DA1EBA6E7E8 /* Pods-Anyway-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Anyway-acknowledgements.plist"; sourceTree = ""; }; + 6DC7EB8E3DE789DDA96E80E38F29D51B /* RLMObjectBase.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObjectBase.h; path = include/RLMObjectBase.h; sourceTree = ""; }; + 6E0F53489BE5523FA7EC41E425BE6733 /* Operators.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Operators.swift; path = Source/Core/Operators.swift; sourceTree = ""; }; + 6E9232B57FCBE99DC654345273A61422 /* ResponseSerialization.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ResponseSerialization.swift; path = Source/ResponseSerialization.swift; sourceTree = ""; }; + 6ED883282453F89464A3E2B47B0D90A6 /* NavigationAccessoryView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NavigationAccessoryView.swift; path = Source/Core/NavigationAccessoryView.swift; sourceTree = ""; }; + 6F4BF5CD03006FF03F3A27B25DC0F87C /* RLMJSONModels.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMJSONModels.m; path = Realm/RLMJSONModels.m; sourceTree = ""; }; + 6FD0A44DB5FB29D4B787E94698D3504A /* LabelRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LabelRow.swift; path = Source/Rows/LabelRow.swift; sourceTree = ""; }; + 6FEABED8562208C6D084A3C7C1B5B913 /* RLMSyncUser.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncUser.h; path = include/RLMSyncUser.h; sourceTree = ""; }; + 702440DC1A628016068AF5189F2DC8C9 /* RLMObjectSchema.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMObjectSchema.mm; path = Realm/RLMObjectSchema.mm; sourceTree = ""; }; + 746A280B033147EDBAD7070E10EC72BC /* Eureka-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Eureka-prefix.pch"; sourceTree = ""; }; + 751759ECB55AEBE77F10C2ACBB5FC5C7 /* MultipleSelectorViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MultipleSelectorViewController.swift; path = Source/Rows/Controllers/MultipleSelectorViewController.swift; sourceTree = ""; }; + 75871C8208172885ED1B2473CD69CFF9 /* sync_user.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = sync_user.cpp; path = Realm/ObjectStore/src/sync/sync_user.cpp; sourceTree = ""; }; + 78D9249A3C56C111F67D87AAFBD4037E /* RLMSwiftSupport.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMSwiftSupport.m; path = Realm/RLMSwiftSupport.m; sourceTree = ""; }; + 78DE9922E322A9120D18498BFF31B499 /* BaseRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BaseRow.swift; path = Source/Core/BaseRow.swift; sourceTree = ""; }; + 79941B3181D74379FD373C31B5C69947 /* Realm-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Realm-prefix.pch"; sourceTree = ""; }; + 79FAFB0B673F3C800E0154FDE6F2BF79 /* RealmCollection.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RealmCollection.swift; path = RealmSwift/RealmCollection.swift; sourceTree = ""; }; + 7A773B69EBB1239A6A18F2BB26781989 /* RLMThreadSafeReference.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMThreadSafeReference.h; path = include/RLMThreadSafeReference.h; sourceTree = ""; }; + 7A8920FC9084B4C9A81116734A9749A0 /* RLMPlatform.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMPlatform.h; path = include/RLMPlatform.h; sourceTree = ""; }; + 7AB110AA2342D66D7CC1EC5DE3A9AF95 /* sync_metadata.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = sync_metadata.cpp; path = Realm/ObjectStore/src/sync/impl/sync_metadata.cpp; sourceTree = ""; }; + 7B5212E251572BA3D8FEF34549E8259A /* HeaderFooterView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HeaderFooterView.swift; path = Source/Core/HeaderFooterView.swift; sourceTree = ""; }; + 7C59C69F2C46DB368087816142770B5E /* RuleLength.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RuleLength.swift; path = Source/Validations/RuleLength.swift; sourceTree = ""; }; + 7CEEBEA40DD326F9EE6940AD39294427 /* RowControllerType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RowControllerType.swift; path = Source/Core/RowControllerType.swift; sourceTree = ""; }; + 7F525C17DDBB7FA46D49579145BDEDFD /* keychain_helper.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = keychain_helper.cpp; path = Realm/ObjectStore/src/impl/apple/keychain_helper.cpp; sourceTree = ""; }; + 800126034A32DA13013907A513953FE1 /* Aliases.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Aliases.swift; path = RealmSwift/Aliases.swift; sourceTree = ""; }; + 80209A2BD2DAA37387B2006851344114 /* RLMSyncCredentials.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncCredentials.h; path = include/RLMSyncCredentials.h; sourceTree = ""; }; + 83A0A28CC02CD6AB04BB51F73F8CB169 /* RLMSyncSubscription.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncSubscription.mm; path = Realm/RLMSyncSubscription.mm; sourceTree = ""; }; + 83DB289513B9755814CCED9C686CFCED /* SwiftyJSON.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwiftyJSON.swift; path = Source/SwiftyJSON.swift; sourceTree = ""; }; + 8438FFD5AE38CCAE72808A74F062ECC8 /* Results.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Results.swift; path = RealmSwift/Results.swift; sourceTree = ""; }; + 84B59231ACC04A09E7261D7431571B3D /* RLMObjectSchema.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObjectSchema.h; path = include/RLMObjectSchema.h; sourceTree = ""; }; + 8570192DA2887EFCB6C6912D70E66D9D /* Alamofire-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Alamofire-prefix.pch"; sourceTree = ""; }; + 864B328D62AF03B02276BECCFDFF9040 /* NetworkReachabilityManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NetworkReachabilityManager.swift; path = Source/NetworkReachabilityManager.swift; sourceTree = ""; }; + 87C392FB2A1558DFB70FDB1D384A0218 /* sync_config.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = sync_config.cpp; path = Realm/ObjectStore/src/sync/sync_config.cpp; sourceTree = ""; }; + 88A609AD0DADD55736F3321FBE23117E /* Property.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Property.swift; path = RealmSwift/Property.swift; sourceTree = ""; }; + 8C81565305966867BC29F784D4A855B6 /* Util.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Util.swift; path = RealmSwift/Util.swift; sourceTree = ""; }; + 8EF03B0A1821B57A4D813FC44E6DA4DB /* RLMRealm_Dynamic.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMRealm_Dynamic.h; path = include/RLMRealm_Dynamic.h; sourceTree = ""; }; + 8FEEFF42B6B8348C08E9D90871789F14 /* RLMOptionalBase.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMOptionalBase.mm; path = Realm/RLMOptionalBase.mm; sourceTree = ""; }; + 90DB17A7AFC7EB4978CF84E262E454D5 /* RLMResults.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMResults.h; path = include/RLMResults.h; sourceTree = ""; }; + 91D870DD127FCCA184837D3AA0A3F503 /* RLMListBase.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMListBase.h; path = include/RLMListBase.h; sourceTree = ""; }; + 924FEE607EDFD95455B17DDB02D0923C /* RLMProperty.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMProperty.h; path = include/RLMProperty.h; sourceTree = ""; }; 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; - 93C123B1B31F0D8126DFA0BD859D2379 /* ListCheckRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ListCheckRow.swift; path = Source/Rows/SelectableRows/ListCheckRow.swift; sourceTree = ""; }; - 956AEA5CF04A5AB3EC1142C5720085C2 /* List.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = List.swift; path = RealmSwift/List.swift; sourceTree = ""; }; - 95F08EA278D9F6361DEB3006F384C389 /* Alamofire.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = Alamofire.modulemap; sourceTree = ""; }; - 9659455EF6CAF526AD0F5AEEC11A5CB7 /* SelectableRowType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SelectableRowType.swift; path = Source/Core/SelectableRowType.swift; sourceTree = ""; }; - 978B783E5ACC1ACAE6353A5AB032D93F /* RuleEmail.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RuleEmail.swift; path = Source/Validations/RuleEmail.swift; sourceTree = ""; }; - 9905EB2DBE88423775498C0762231D31 /* RLMSyncPermissionOffer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncPermissionOffer.h; path = include/RLMSyncPermissionOffer.h; sourceTree = ""; }; - 9AA29F62615D2D85B1E418BED094A73C /* RuleRequired.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RuleRequired.swift; path = Source/Validations/RuleRequired.swift; sourceTree = ""; }; + 93D93C3ED858076114B56C5751004996 /* DateRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DateRow.swift; path = Source/Rows/DateRow.swift; sourceTree = ""; }; + 93E450EF6884F59FE08FF8AF0362C88C /* SwipeActions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwipeActions.swift; path = Source/Core/SwipeActions.swift; sourceTree = ""; }; + 9458CF5DCA16A9827FA2C7D0DC6812FE /* SessionManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SessionManager.swift; path = Source/SessionManager.swift; sourceTree = ""; }; + 9486BD497E3820245DCFD08D07285B0A /* sync_session.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = sync_session.cpp; path = Realm/ObjectStore/src/sync/sync_session.cpp; sourceTree = ""; }; + 95645FF346EE5177388A4C1D971F81E4 /* transact_log_handler.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = transact_log_handler.cpp; path = Realm/ObjectStore/src/impl/transact_log_handler.cpp; sourceTree = ""; }; + 95EF6D5795A0383AB2DC902E386A774B /* SwitchRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwitchRow.swift; path = Source/Rows/SwitchRow.swift; sourceTree = ""; }; + 986CA011E61CAC25F855201406097F66 /* ParameterEncoding.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ParameterEncoding.swift; path = Source/ParameterEncoding.swift; sourceTree = ""; }; 9B5208DCDB0DE3DC05643C7599AF1D02 /* SwiftyJSON.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = SwiftyJSON.framework; path = SwiftyJSON.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 9E4FF1360A4607377FBB5B1B3AFEEA7A /* collection_notifications.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = collection_notifications.cpp; path = Realm/ObjectStore/src/collection_notifications.cpp; sourceTree = ""; }; - 9E5E772C1B03083A40994D3FD70D5426 /* RLMThreadSafeReference.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMThreadSafeReference.mm; path = Realm/RLMThreadSafeReference.mm; sourceTree = ""; }; - 9F59515DA257EEB12738DD612E21D316 /* sync_metadata.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = sync_metadata.cpp; path = Realm/ObjectStore/src/sync/impl/sync_metadata.cpp; sourceTree = ""; }; - A12F78C2F68E04E316FE62951B25D169 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - A1A043D377AAD1E52073686C96FF4CB6 /* Request.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Request.swift; path = Source/Request.swift; sourceTree = ""; }; - A1C7E0F431B5E7F5D1489E545A37E07D /* SwiftyJSON-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SwiftyJSON-dummy.m"; sourceTree = ""; }; - A1FABEEC135187CA74F85B3F768148C3 /* RLMListBase.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMListBase.h; path = include/RLMListBase.h; sourceTree = ""; }; - A2D088C2DCE81B9C32CAFE3F16D7003F /* sync_manager.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = sync_manager.cpp; path = Realm/ObjectStore/src/sync/sync_manager.cpp; sourceTree = ""; }; - A2E62568C016C9FD6DA9E44A207C7B9C /* RLMObject.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMObject.mm; path = Realm/RLMObject.mm; sourceTree = ""; }; - A3E19E8BDBA82BE8168AE37FA9C1BF21 /* Pods-Anyway.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Anyway.release.xcconfig"; sourceTree = ""; }; - A553DF821B37979C37BC0E3263C3F6A8 /* RLMProperty_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMProperty_Private.h; path = include/RLMProperty_Private.h; sourceTree = ""; }; - A57A6AE790A439930CD0BB306CBFAEF7 /* Alamofire-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Alamofire-prefix.pch"; sourceTree = ""; }; - A7E633301E8BB0C26474AAA924E84975 /* RLMConstants.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMConstants.m; path = Realm/RLMConstants.m; sourceTree = ""; }; - A86C68694ADEB912CBAE30E04992C31C /* Pods-Anyway-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Anyway-resources.sh"; sourceTree = ""; }; - A9C3ECEAF6DD8DD56BC4C3B6BBF571B5 /* RLMSchema_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSchema_Private.h; path = include/RLMSchema_Private.h; sourceTree = ""; }; - AAD4FBA1295E2A108DB8344445050359 /* SwitchRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwitchRow.swift; path = Source/Rows/SwitchRow.swift; sourceTree = ""; }; - AC10336CC1E93B42D3F920ED569F5259 /* RLMConstants.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMConstants.h; path = include/RLMConstants.h; sourceTree = ""; }; - AC6BDBD16312E5555C4A809908EE5F11 /* SelectableSection.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SelectableSection.swift; path = Source/Core/SelectableSection.swift; sourceTree = ""; }; - ACEE2EB466686DFBBEF9A8D83DDF4BB7 /* RLMListBase.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMListBase.mm; path = Realm/RLMListBase.mm; sourceTree = ""; }; - AF277B8952F3E92239B0266AEFEB566D /* Operators.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Operators.swift; path = Source/Core/Operators.swift; sourceTree = ""; }; - B011D58D42FE700BD7FBE2FA010DA61B /* Alamofire-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Alamofire-umbrella.h"; sourceTree = ""; }; - B07454B0F2C1553B654398EC391025D6 /* Error.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Error.swift; path = RealmSwift/Error.swift; sourceTree = ""; }; - B3C815EEAB1A46D4EB01E54A27B22C0A /* sync_user.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = sync_user.cpp; path = Realm/ObjectStore/src/sync/sync_user.cpp; sourceTree = ""; }; - B46433D888982EFBB459A99558D4D810 /* Realm-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Realm-dummy.m"; sourceTree = ""; }; - B4A4FBE832D1BAFB8C77FA4082C6B113 /* TaskDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TaskDelegate.swift; path = Source/TaskDelegate.swift; sourceTree = ""; }; - B5AB4446CFFCD1BF3787BBC4D68654CB /* RealmSwift-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "RealmSwift-dummy.m"; sourceTree = ""; }; - B7EA768DC3032A622F5542785D38139F /* Notifications.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Notifications.swift; path = Source/Notifications.swift; sourceTree = ""; }; - B8DC206D5ABFFD9DCC07EA5B9A28D151 /* SessionManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SessionManager.swift; path = Source/SessionManager.swift; sourceTree = ""; }; - BA2FDCFC499C20ED3E774D1316DC884E /* LinkingObjects.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LinkingObjects.swift; path = RealmSwift/LinkingObjects.swift; sourceTree = ""; }; + 9B578B60A62261F86478ACD049E8E436 /* Result.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Result.swift; path = Source/Result.swift; sourceTree = ""; }; + 9D4C701C8D05720E98DE9B454811B7C4 /* PushRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PushRow.swift; path = Source/Rows/PushRow.swift; sourceTree = ""; }; + 9F51A7E1B01B2BB97ADBB863BD6D4302 /* RLMRealm.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMRealm.mm; path = Realm/RLMRealm.mm; sourceTree = ""; }; + A0DA0FEA1E28CCA7CC5072098C38DC6F /* Pods-Anyway.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Anyway.debug.xcconfig"; sourceTree = ""; }; + A0F0AF7305F387DC1EB5B808BFDB0C0B /* AlertRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AlertRow.swift; path = Source/Rows/AlertRow.swift; sourceTree = ""; }; + A22A60BC550E9C333E9F1B38A5505AA9 /* DecimalFormatter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DecimalFormatter.swift; path = Source/Rows/Common/DecimalFormatter.swift; sourceTree = ""; }; + A23EF01AB8B863A114F893F1DE53FA9E /* primitive_list_notifier.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = primitive_list_notifier.cpp; path = Realm/ObjectStore/src/impl/primitive_list_notifier.cpp; sourceTree = ""; }; + A406C04056749BE399A669BAD6983298 /* librealmcore-ios.a */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = archive.ar; name = "librealmcore-ios.a"; path = "core/librealmcore-ios.a"; sourceTree = ""; }; + A7B22BDEBAE7E859384071ABBC95CAA2 /* Pods-Anyway-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-Anyway-acknowledgements.markdown"; sourceTree = ""; }; + A7BCA7A83702D9E86CDA34631864173C /* RLMConstants.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMConstants.m; path = Realm/RLMConstants.m; sourceTree = ""; }; + AA392BA26630759D689717E517366E41 /* SwiftyJSON.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftyJSON.xcconfig; sourceTree = ""; }; + AA54227EA9DEF8A1F486F1ECE61992F0 /* Alamofire-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Alamofire-umbrella.h"; sourceTree = ""; }; + AA691D57062AA682DE09170C69A1406D /* Core.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Core.swift; path = Source/Core/Core.swift; sourceTree = ""; }; + ABA23A5018A81ACC4DC60114F737BDAA /* DispatchQueue+Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "DispatchQueue+Alamofire.swift"; path = "Source/DispatchQueue+Alamofire.swift"; sourceTree = ""; }; + AF71550B6E0D71EE6264D5EFB88B3B16 /* SortDescriptor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SortDescriptor.swift; path = RealmSwift/SortDescriptor.swift; sourceTree = ""; }; + AF9E9D7F40ED5AAC52781E5915DFB4B9 /* FieldsRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FieldsRow.swift; path = Source/Rows/FieldsRow.swift; sourceTree = ""; }; + AFA7FB6D6175EBF3AA8DDB331F9C8083 /* RLMObjectSchema_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObjectSchema_Private.h; path = include/RLMObjectSchema_Private.h; sourceTree = ""; }; + B01986AB41A7D9DFFB3AC8DF744D2008 /* SelectableSection.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SelectableSection.swift; path = Source/Core/SelectableSection.swift; sourceTree = ""; }; + B06D8074680CFCAEF9159D23514BC4AE /* SwiftVersion.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwiftVersion.swift; path = RealmSwift/SwiftVersion.swift; sourceTree = ""; }; + B1DC356638CEC2013EB2AA5C48324795 /* NSError+RLMSync.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSError+RLMSync.h"; path = "include/NSError+RLMSync.h"; sourceTree = ""; }; + B352EAE0150B12A4EBDA9B7420273246 /* Realm.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Realm.swift; path = RealmSwift/Realm.swift; sourceTree = ""; }; + B3886B0BB14B2EF5C4262922ECF3DE2E /* RLMObject.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObject.h; path = include/RLMObject.h; sourceTree = ""; }; + B5DAAF083C1981C7AC6747B2D61DC84B /* SwiftyJSON-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftyJSON-umbrella.h"; sourceTree = ""; }; + B6979907A286F1FF396BC34194D33F73 /* RLMSyncSession.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncSession.mm; path = Realm/RLMSyncSession.mm; sourceTree = ""; }; + B6D34AA8D31CDEF7005ED43E1DE58E5D /* RLMNetworkClient.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMNetworkClient.mm; path = Realm/RLMNetworkClient.mm; sourceTree = ""; }; + B726C060B1B3118F74436CE2D554AB90 /* Validation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Validation.swift; path = Source/Validation.swift; sourceTree = ""; }; + B7634052748874DCB824BA021EAC0031 /* InlineRowType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = InlineRowType.swift; path = Source/Core/InlineRowType.swift; sourceTree = ""; }; + B8CDAC6FCDFA06F5D625331F21F5F7FB /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; }; + BA0C8517B53BB218529BE33B0282E3B1 /* RLMSyncConfiguration_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncConfiguration_Private.h; path = include/RLMSyncConfiguration_Private.h; sourceTree = ""; }; + BB2B4D0F33C6541E253E580A9771D48B /* RLMOptionalBase.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMOptionalBase.h; path = include/RLMOptionalBase.h; sourceTree = ""; }; + BB69FC881A589171D4EA27D5D89DBAF3 /* RLMArray.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMArray.mm; path = Realm/RLMArray.mm; sourceTree = ""; }; BBCD9F69786420E715F3D7F63EDF2826 /* Realm.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Realm.framework; path = Realm.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - BCFFE113430B44131E8A2FFE5C595B78 /* SwiftyJSON.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwiftyJSON.swift; path = Source/SwiftyJSON.swift; sourceTree = ""; }; - BE125D3EAB2AD7398DA1E3D3A7267498 /* RowControllerType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RowControllerType.swift; path = Source/Core/RowControllerType.swift; sourceTree = ""; }; - BE393BF220453660A254404E054F0FAD /* RLMAccessor.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMAccessor.mm; path = Realm/RLMAccessor.mm; sourceTree = ""; }; - BE87BD78CE2474F4AA9F7DDF39CC8A79 /* RLMRealm.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMRealm.mm; path = Realm/RLMRealm.mm; sourceTree = ""; }; - BEE43652C132B102F2A932B2491BD544 /* RLMUpdateChecker.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMUpdateChecker.mm; path = Realm/RLMUpdateChecker.mm; sourceTree = ""; }; - BEF6D640145744CB8BEB33726BCB7547 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.0.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; - BF3CF670586246BAEC381F6EB258D7C1 /* RLMSyncUser.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncUser.mm; path = Realm/RLMSyncUser.mm; sourceTree = ""; }; - C0728F2E5AE5F61D049A15932F78BA19 /* RLMObject.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObject.h; path = include/RLMObject.h; sourceTree = ""; }; - C08AC3525CE6AE6E9A38F18D070EDF56 /* RLMClassInfo.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMClassInfo.mm; path = Realm/RLMClassInfo.mm; sourceTree = ""; }; - C1FA3F092353477F953E1F680C029226 /* ResponseSerialization.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ResponseSerialization.swift; path = Source/ResponseSerialization.swift; sourceTree = ""; }; - C4F3E40991A37DCB4879656F07CC9B31 /* ButtonRowWithPresent.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ButtonRowWithPresent.swift; path = Source/Rows/ButtonRowWithPresent.swift; sourceTree = ""; }; - C7BC9AA4FF20F481CD6316CF967E7E44 /* HeaderFooterView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HeaderFooterView.swift; path = Source/Core/HeaderFooterView.swift; sourceTree = ""; }; - C83BDBC64DA966D95C5E7985BF558A63 /* Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Alamofire.swift; path = Source/Alamofire.swift; sourceTree = ""; }; - C89A08A820500A904B6233379A9817E7 /* RealmConfiguration.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RealmConfiguration.swift; path = RealmSwift/RealmConfiguration.swift; sourceTree = ""; }; - C9F4EAB7ADB9F943D14DC790F2116D94 /* RLMSchema.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSchema.h; path = include/RLMSchema.h; sourceTree = ""; }; - CA60E1705211357CF6C402D21D847134 /* Protocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Protocols.swift; path = Source/Rows/Common/Protocols.swift; sourceTree = ""; }; - CAD6BFE59B3D53D339C52EE758C9E1B6 /* DatePickerRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DatePickerRow.swift; path = Source/Rows/DatePickerRow.swift; sourceTree = ""; }; - CB147AC31F137C05E67DC35CBB4EED2E /* SelectorAlertController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SelectorAlertController.swift; path = Source/Rows/Controllers/SelectorAlertController.swift; sourceTree = ""; }; - CB30E2CDA387263CFF9EA9C05C5218E3 /* RLMObjectStore.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMObjectStore.mm; path = Realm/RLMObjectStore.mm; sourceTree = ""; }; - CB78819040B9EC5BA9828CEA7B9C979C /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - CE12DBCCD12A2606CBE168BC27DAEEFF /* RLMSyncConfiguration_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncConfiguration_Private.h; path = include/RLMSyncConfiguration_Private.h; sourceTree = ""; }; - CE35665E296651539A94F942B81F95EE /* RLMOptionalBase.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMOptionalBase.h; path = include/RLMOptionalBase.h; sourceTree = ""; }; - CFFD4E41A1EF23F20AAD44BC3F359A6A /* thread_safe_reference.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = thread_safe_reference.cpp; path = Realm/ObjectStore/src/thread_safe_reference.cpp; sourceTree = ""; }; - D127167FD3356138E35CD696019AFDAC /* OptionsRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OptionsRow.swift; path = Source/Rows/Common/OptionsRow.swift; sourceTree = ""; }; - D19A6659DD24000C3DCCE171456D16F2 /* Schema.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Schema.swift; path = RealmSwift/Schema.swift; sourceTree = ""; }; - D2A9F3F21B7819EF65B2FF6F110AF533 /* RLMPlatform.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMPlatform.h; path = include/RLMPlatform.h; sourceTree = ""; }; - D2EF20A785AA2BDD2D0C2BF1D987A6DB /* Form.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Form.swift; path = Source/Core/Form.swift; sourceTree = ""; }; - D3646CE1E7000B6E5DACD713D0610F22 /* DateInlineRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DateInlineRow.swift; path = Source/Rows/DateInlineRow.swift; sourceTree = ""; }; - D3B238037C8AC4D64934FAEBE88C61DA /* keychain_helper.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = keychain_helper.cpp; path = Realm/ObjectStore/src/impl/apple/keychain_helper.cpp; sourceTree = ""; }; - D4CDCDC351D38138ECCED267801C5CBF /* RLMMigration.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMMigration.mm; path = Realm/RLMMigration.mm; sourceTree = ""; }; - D4D7ECFF3A64CC20590DFD4824E6DFC4 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - D53FB91EC150CA439DF95B39FE6543C6 /* RuleRange.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RuleRange.swift; path = Source/Validations/RuleRange.swift; sourceTree = ""; }; - D5ED347940A6390F828FAA476C837545 /* RLMSyncPermissionOfferResponse.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncPermissionOfferResponse.h; path = include/RLMSyncPermissionOfferResponse.h; sourceTree = ""; }; - D6D779DD4034A34EB92387F98B71760D /* RLMSyncErrorResponseModel.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMSyncErrorResponseModel.m; path = Realm/RLMSyncErrorResponseModel.m; sourceTree = ""; }; - D71878C452FE04FD80E0D2BE0842646A /* Aliases.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Aliases.swift; path = RealmSwift/Aliases.swift; sourceTree = ""; }; - D824F4C8D4EFE3AEE8781A238BF06950 /* object_store.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = object_store.cpp; path = Realm/ObjectStore/src/object_store.cpp; sourceTree = ""; }; - D9B2A16F6F51E0E5E465E8DF9B960BB6 /* MultipleSelectorRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MultipleSelectorRow.swift; path = Source/Rows/MultipleSelectorRow.swift; sourceTree = ""; }; - DA6E5A8C807057AA6361F8C7A5700BFE /* Validation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Validation.swift; path = Source/Core/Validation.swift; sourceTree = ""; }; - DC6C3BEF7CF735F64C406CEE2FE2170E /* Pods-Anyway-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-Anyway-umbrella.h"; sourceTree = ""; }; - DD4813B305768312DFCC46E17B165E9D /* RLMSyncPermissionChange.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncPermissionChange.h; path = include/RLMSyncPermissionChange.h; sourceTree = ""; }; - DDA629004C61F9A1F688221E581BD35C /* transact_log_handler.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = transact_log_handler.cpp; path = Realm/ObjectStore/src/impl/transact_log_handler.cpp; sourceTree = ""; }; - DF96E6C5E79D7F0FFA45A108EE7E3CB6 /* PickerInputRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PickerInputRow.swift; path = Source/Rows/PickerInputRow.swift; sourceTree = ""; }; - E06D431FD333C8EA06E4F2DF70DA7D76 /* AFError.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AFError.swift; path = Source/AFError.swift; sourceTree = ""; }; + BBFCD450C7395E074EBFDD409E511993 /* RowProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RowProtocols.swift; path = Source/Core/RowProtocols.swift; sourceTree = ""; }; + BC703D03A42D8D2D25BCE9D7CE1DC68D /* ButtonRowWithPresent.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ButtonRowWithPresent.swift; path = Source/Rows/ButtonRowWithPresent.swift; sourceTree = ""; }; + BE22EFE05EAF99DD2057C42C33758987 /* RuleClosure.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RuleClosure.swift; path = Source/Validations/RuleClosure.swift; sourceTree = ""; }; + BE2AD210BCB92444548FF2D08C23D017 /* RLMSchema.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSchema.mm; path = Realm/RLMSchema.mm; sourceTree = ""; }; + BE8FD25136279F7A06411526D1F4CEEF /* sync_manager.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = sync_manager.cpp; path = Realm/ObjectStore/src/sync/sync_manager.cpp; sourceTree = ""; }; + BF9BFF632E3DD6218BC5959537D0763A /* TriplePickerInputRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TriplePickerInputRow.swift; path = Source/Rows/TriplePickerInputRow.swift; sourceTree = ""; }; + BFC0EB7C7CE270DD215E17D9351C311A /* RLMMigration_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMMigration_Private.h; path = include/RLMMigration_Private.h; sourceTree = ""; }; + C1447446CBA5E861394374981B4CE394 /* shared_realm.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = shared_realm.cpp; path = Realm/ObjectStore/src/shared_realm.cpp; sourceTree = ""; }; + C1A6B66006D194520803DE174BC1EC19 /* RLMObjectStore.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMObjectStore.mm; path = Realm/RLMObjectStore.mm; sourceTree = ""; }; + C28F3F6AA1DB3A5174EE5A62B1F2A3CD /* PresenterRowType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PresenterRowType.swift; path = Source/Core/PresenterRowType.swift; sourceTree = ""; }; + C36443BD174010DA804B31FBA85C761D /* RLMObjectStore.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObjectStore.h; path = include/RLMObjectStore.h; sourceTree = ""; }; + C3D5A37CD39E1E15FB43F26E2D9F7786 /* RLMObjectBase_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObjectBase_Private.h; path = include/RLMObjectBase_Private.h; sourceTree = ""; }; + C55AD46B8ACAF57640C65CA2745B13BA /* RLMRealmUtil.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMRealmUtil.mm; path = Realm/RLMRealmUtil.mm; sourceTree = ""; }; + C78E593BBD7D760BCB9C41D3CF56CB69 /* Alamofire.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = Alamofire.modulemap; sourceTree = ""; }; + C84FD9C200D57EE2834B9EF29FA80553 /* Pods-Anyway-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-Anyway-umbrella.h"; sourceTree = ""; }; + C95C068012D70E696D3A2ADBBDB27E35 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + C98E314F648A32E9F16CCEF7FB450E16 /* RLMRealmConfiguration+Sync.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "RLMRealmConfiguration+Sync.mm"; path = "Realm/RLMRealmConfiguration+Sync.mm"; sourceTree = ""; }; + CA44D018B045CD243BEC0940E37ED73B /* Pods-Anyway.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-Anyway.modulemap"; sourceTree = ""; }; + CA74C2E8C385452EE0A250F6225371B0 /* SelectableRowType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SelectableRowType.swift; path = Source/Core/SelectableRowType.swift; sourceTree = ""; }; + CB5C94E9B607AB4BB4B946482BFA6B8F /* RLMRealmConfiguration+Sync.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "RLMRealmConfiguration+Sync.h"; path = "include/RLMRealmConfiguration+Sync.h"; sourceTree = ""; }; + CC33867D8B53A2679CF5D156B7132E9B /* RLMObjectBase_Dynamic.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObjectBase_Dynamic.h; path = include/RLMObjectBase_Dynamic.h; sourceTree = ""; }; + CD99ABACAA310323AEDC1AE7A9E65321 /* RLMProperty.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMProperty.mm; path = Realm/RLMProperty.mm; sourceTree = ""; }; + CE239D642911B6F39732B46D5AEC203E /* TaskDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TaskDelegate.swift; path = Source/TaskDelegate.swift; sourceTree = ""; }; + CE4D9A2CB050B8F48137F0CB588079D7 /* RLMClassInfo.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMClassInfo.mm; path = Realm/RLMClassInfo.mm; sourceTree = ""; }; + D1091D628F3A1E41BA2BCD0AAD0190A3 /* collection_notifier.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = collection_notifier.cpp; path = Realm/ObjectStore/src/impl/collection_notifier.cpp; sourceTree = ""; }; + D1B7F7FE8842DA2A2915B46BA27D96EF /* binding_callback_thread_observer.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = binding_callback_thread_observer.cpp; path = Realm/ObjectStore/src/binding_callback_thread_observer.cpp; sourceTree = ""; }; + D36EF80003D71394190566A94C169AA0 /* Eureka-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Eureka-umbrella.h"; sourceTree = ""; }; + D386F72C81BB35F7759273D64401BA4E /* RLMCollection.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMCollection.mm; path = Realm/RLMCollection.mm; sourceTree = ""; }; + D53168BD4DCE3E1733D2B567714F78D4 /* thread_safe_reference.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = thread_safe_reference.cpp; path = Realm/ObjectStore/src/thread_safe_reference.cpp; sourceTree = ""; }; + D59D03A16268D980788997D03C5F8D89 /* RLMObject.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMObject.mm; path = Realm/RLMObject.mm; sourceTree = ""; }; + D6A6499C4A8C3B77D08A8CC89C3A2CD9 /* Section.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Section.swift; path = Source/Core/Section.swift; sourceTree = ""; }; + D6AAF42155764E91D966EFE018D4F18A /* object_schema.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = object_schema.cpp; path = Realm/ObjectStore/src/object_schema.cpp; sourceTree = ""; }; + D710B0CC80C2FDA501FD4F5D2FC52B2D /* RLMCollection_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMCollection_Private.h; path = include/RLMCollection_Private.h; sourceTree = ""; }; + DB06C6D64D9A14CABD9501EF856F74C3 /* external_commit_helper.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = external_commit_helper.cpp; path = Realm/ObjectStore/src/impl/apple/external_commit_helper.cpp; sourceTree = ""; }; + DB7795DEDC3688BE80EBE0633A325028 /* RLMAccessor.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMAccessor.mm; path = Realm/RLMAccessor.mm; sourceTree = ""; }; + DBB517B3130AECB9E6AC13567F48BC38 /* object_store.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = object_store.cpp; path = Realm/ObjectStore/src/object_store.cpp; sourceTree = ""; }; + DC698CDA7D7633277D18E4E2DDFBECA8 /* system_configuration.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = system_configuration.cpp; path = Realm/ObjectStore/src/sync/impl/apple/system_configuration.cpp; sourceTree = ""; }; + DD6F999DFB1EB5A441AF482A5FC81007 /* OptionsRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OptionsRow.swift; path = Source/Rows/Common/OptionsRow.swift; sourceTree = ""; }; + DE71E71F838918B77F921956DCAEAA0A /* RLMRealmConfiguration_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMRealmConfiguration_Private.h; path = include/RLMRealmConfiguration_Private.h; sourceTree = ""; }; + E0010B5FFEB587C83EA43BD6E57F6D9F /* Eureka.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = Eureka.modulemap; sourceTree = ""; }; + E0BF74DA8204936480F9E9798F1CA3BC /* PickerRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PickerRow.swift; path = Source/Rows/PickerRow.swift; sourceTree = ""; }; E109A6BA1FE2D5E166A4DDF92C551CD0 /* RealmSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = RealmSwift.framework; path = RealmSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - E3D815140621A399552C48BF97759F23 /* DateRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DateRow.swift; path = Source/Rows/DateRow.swift; sourceTree = ""; }; - E4CAF02FDE199C9936F3AAB9F5E80BA8 /* Realm.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Realm.h; path = include/Realm.h; sourceTree = ""; }; - E665E84C8E4BB0E3803FB153ADF872E7 /* RealmCollection.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RealmCollection.swift; path = RealmSwift/RealmCollection.swift; sourceTree = ""; }; - E829C2D738568B2DD7488FB084BD4EC1 /* RLMSyncPermissionOfferResponse.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMSyncPermissionOfferResponse.m; path = Realm/RLMSyncPermissionOfferResponse.m; sourceTree = ""; }; - E9A4F74F141D09D7CD5998FAA9A1870D /* object_schema.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = object_schema.cpp; path = Realm/ObjectStore/src/object_schema.cpp; sourceTree = ""; }; - EB23711C1CEC6CF2337C5819D60BF159 /* collection_notifier.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = collection_notifier.cpp; path = Realm/ObjectStore/src/impl/collection_notifier.cpp; sourceTree = ""; }; - EB338157B00F36FB6060E70B536BDF66 /* RLMObjectSchema_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObjectSchema_Private.h; path = include/RLMObjectSchema_Private.h; sourceTree = ""; }; - EC3368D01A978261649708EACBBC2E61 /* ButtonRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ButtonRow.swift; path = Source/Rows/ButtonRow.swift; sourceTree = ""; }; - EEEAC4F001764E87237EFCC8BC4A705C /* RLMSyncManager.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncManager.mm; path = Realm/RLMSyncManager.mm; sourceTree = ""; }; - EEFB9FD1287C8247D8948A9FBCB2846A /* SwiftVersion.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwiftVersion.swift; path = RealmSwift/SwiftVersion.swift; sourceTree = ""; }; - EF7DE4D98DFB45FBCEAA827C87595DE6 /* RLMQueryUtil.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMQueryUtil.mm; path = Realm/RLMQueryUtil.mm; sourceTree = ""; }; - EF964A1E8476F05A9A9F4A4D6259A1DD /* RLMCollection.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMCollection.mm; path = Realm/RLMCollection.mm; sourceTree = ""; }; - F1E602FA60F24443233E5EC8692D47B1 /* SwiftyJSON-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftyJSON-prefix.pch"; sourceTree = ""; }; - F5203DAF180A9A8801705F411B711B01 /* Pods-Anyway-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-Anyway-dummy.m"; sourceTree = ""; }; - F52E1A34E1E348A3265431E4106847D9 /* Eureka-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Eureka-dummy.m"; sourceTree = ""; }; - F587B0BB1761780DA10D5C25BFAB5DDC /* Row.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Row.swift; path = Source/Core/Row.swift; sourceTree = ""; }; - F633BCB79FCBA0377A53C7E63ED27D02 /* RLMTokenModels.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMTokenModels.m; path = Realm/RLMTokenModels.m; sourceTree = ""; }; - F6A81900E2DEB7EFBC9DFAAD9B91F191 /* RealmSwift.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = RealmSwift.xcconfig; sourceTree = ""; }; - F7B32E1E31A1D58627A01759618B4D79 /* BaseRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BaseRow.swift; path = Source/Core/BaseRow.swift; sourceTree = ""; }; - F8225E11B668E56A71D602CB391CBF5C /* TextAreaRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TextAreaRow.swift; path = Source/Rows/TextAreaRow.swift; sourceTree = ""; }; - F84F232C37EF8A2DD0C9FF8DD9ADFC08 /* RuleRegExp.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RuleRegExp.swift; path = Source/Validations/RuleRegExp.swift; sourceTree = ""; }; - F90EDF64FEF32A1E26029BB85ED1C56E /* RLMSyncCredentials.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMSyncCredentials.m; path = Realm/RLMSyncCredentials.m; sourceTree = ""; }; - F98A06DF392EC6DD6830BF5AA4AF9442 /* Pods-Anyway-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Anyway-frameworks.sh"; sourceTree = ""; }; - FA25F698977EFDD9705EDE8D7263E3B1 /* RLMRealmUtil.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMRealmUtil.mm; path = Realm/RLMRealmUtil.mm; sourceTree = ""; }; - FC0C3EE90D3935E7766DDB5EF134852F /* GenericMultipleSelectorRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GenericMultipleSelectorRow.swift; path = Source/Rows/Common/GenericMultipleSelectorRow.swift; sourceTree = ""; }; - FD09069DAF0A3339E871F4D51BE6BB73 /* RuleLength.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RuleLength.swift; path = Source/Validations/RuleLength.swift; sourceTree = ""; }; - FDBD80532A14DC83B71C9D9D1B548103 /* RLMObjectSchema.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObjectSchema.h; path = include/RLMObjectSchema.h; sourceTree = ""; }; - FEA2ED6C6EA03296AF7255E16C77C90B /* RLMSyncUtil.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncUtil.h; path = include/RLMSyncUtil.h; sourceTree = ""; }; - FFF83427A564921CED22CDEB18ECCD69 /* Alamofire.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Alamofire.xcconfig; sourceTree = ""; }; + E2DF58383A66F988377F979F6E35039D /* RLMMigration.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMMigration.mm; path = Realm/RLMMigration.mm; sourceTree = ""; }; + E40D41B07E90F49DF435B9EB8603D8E8 /* realm_coordinator.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = realm_coordinator.cpp; path = Realm/ObjectStore/src/impl/realm_coordinator.cpp; sourceTree = ""; }; + E4BE8DAE4F5784F3878092A687D0F7FE /* DateInlineRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DateInlineRow.swift; path = Source/Rows/DateInlineRow.swift; sourceTree = ""; }; + E4F426F9466D985EEFDD180245D48E94 /* SwiftyJSON-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftyJSON-prefix.pch"; sourceTree = ""; }; + E577F23C05487C0A645ECB9920D9905C /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + E59898BDF46BA9F2CBE2DF02E4B92D42 /* MultipleSelectorRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MultipleSelectorRow.swift; path = Source/Rows/MultipleSelectorRow.swift; sourceTree = ""; }; + E6D52004D92F7AF737CB6373645DB781 /* Optional.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Optional.swift; path = RealmSwift/Optional.swift; sourceTree = ""; }; + E820F2BE88277EBA52781D57A928B924 /* SelectorViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SelectorViewController.swift; path = Source/Rows/Controllers/SelectorViewController.swift; sourceTree = ""; }; + E8A82C9D0BBFA86E32BCC4120D292EF3 /* RLMConstants.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMConstants.h; path = include/RLMConstants.h; sourceTree = ""; }; + E99EA87D64192E461F9D10E54BA19CEF /* ListCheckRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ListCheckRow.swift; path = Source/Rows/SelectableRows/ListCheckRow.swift; sourceTree = ""; }; + EA6F7A4BEB49F3A40F3463280C8AA5FD /* GenericMultipleSelectorRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GenericMultipleSelectorRow.swift; path = Source/Rows/Common/GenericMultipleSelectorRow.swift; sourceTree = ""; }; + EA80AE3316691FF6213775C016E85D07 /* RLMArray_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMArray_Private.h; path = include/RLMArray_Private.h; sourceTree = ""; }; + EB9446C2B4219C267A6A53A4A1FA6017 /* RealmSwift.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = RealmSwift.xcconfig; sourceTree = ""; }; + ED2C32B64ED0E893C1E80242A025A8B6 /* sync_permission.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = sync_permission.cpp; path = Realm/ObjectStore/src/sync/sync_permission.cpp; sourceTree = ""; }; + ED7F0E7D00FF705E8A813AE82E4FB94F /* work_queue.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = work_queue.cpp; path = Realm/ObjectStore/src/sync/impl/work_queue.cpp; sourceTree = ""; }; + EE7100D4160D470F3B510553F389469F /* object.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = object.cpp; path = Realm/ObjectStore/src/object.cpp; sourceTree = ""; }; + EEB08D83627669E22B8E0E7D16CDAFB1 /* RLMProperty_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMProperty_Private.h; path = include/RLMProperty_Private.h; sourceTree = ""; }; + EF7F18C6A90F1DD1BE53BCE4102A40B4 /* RLMRealm.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMRealm.h; path = include/RLMRealm.h; sourceTree = ""; }; + F0664560BA9CC6CBF5068252B6986EE9 /* MultipartFormData.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MultipartFormData.swift; path = Source/MultipartFormData.swift; sourceTree = ""; }; + F1CA9182A041E4565AB6FEA21C6D2421 /* Request.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Request.swift; path = Source/Request.swift; sourceTree = ""; }; + F1F70F6236B9263466A90E34971C9A5F /* Eureka.bundle */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "wrapper.plug-in"; name = Eureka.bundle; path = Source/Resources/Eureka.bundle; sourceTree = ""; }; + F2D46A47CEAE656BFDBD8C38050C1226 /* RuleRegExp.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RuleRegExp.swift; path = Source/Validations/RuleRegExp.swift; sourceTree = ""; }; + F42A8422312B929411F5B93DEEEFA7F8 /* RLMRealm+Sync.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "RLMRealm+Sync.h"; path = "include/RLMRealm+Sync.h"; sourceTree = ""; }; + F5FD6F5D01BF40B3873D41F079FE5595 /* ActionSheetRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ActionSheetRow.swift; path = Source/Rows/ActionSheetRow.swift; sourceTree = ""; }; + F7172D19BC41F018204B6397B905F0CE /* RLMUpdateChecker.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMUpdateChecker.mm; path = Realm/RLMUpdateChecker.mm; sourceTree = ""; }; + F7B108A8D73BBF19C20D7D63650FDE14 /* RLMSyncUtil.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncUtil.mm; path = Realm/RLMSyncUtil.mm; sourceTree = ""; }; + F89F58C07805D6959B4FDC1B5212344F /* Pods-Anyway-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Anyway-frameworks.sh"; sourceTree = ""; }; + FA0B007065C889BDFFC676FEFBB11F6F /* DatePickerRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DatePickerRow.swift; path = Source/Rows/DatePickerRow.swift; sourceTree = ""; }; + FAC63F855BA43317E954BD3FE6CA57B9 /* CheckRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CheckRow.swift; path = Source/Rows/CheckRow.swift; sourceTree = ""; }; + FBDB3F8F7215ED51DA8A86CF19D1AA9C /* SegmentedRow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SegmentedRow.swift; path = Source/Rows/SegmentedRow.swift; sourceTree = ""; }; + FD6809E2C37CAAEF32341A838B340ED8 /* RLMResults_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMResults_Private.h; path = include/RLMResults_Private.h; sourceTree = ""; }; + FF5A889E69302012309706AA00CEA87E /* Pods-Anyway.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Anyway.release.xcconfig"; sourceTree = ""; }; + FF71C7FE31DAA56DDEF5093CEFFB2961 /* Error.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Error.swift; path = RealmSwift/Error.swift; sourceTree = ""; }; + FFB905F3B3B079C5759DDF3A00470EE4 /* AFError.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AFError.swift; path = Source/AFError.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 277B5B91BF09E8DCBA6F28E123B04559 /* Frameworks */ = { + 1ACE4902F7147CAD3CED9E1B2C0BB634 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 53986046D32ECF5B83F5092E7273B01C /* Foundation.framework in Frameworks */, + 4295A1F2F7F1C35B45BAE99178B4D187 /* Foundation.framework in Frameworks */, + E8AF429EAB62FDC15EFFB474DFC8B20A /* Security.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -692,7 +717,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - C70833175BDF0C7B69DAC6BC96C9B017 /* Foundation.framework in Frameworks */, + 7C57457B4CBFF0145014D87782AE7D34 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -700,7 +725,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 64493EC4ED58A74CD0E2F8E9D7CAF8BD /* Foundation.framework in Frameworks */, + 2D0CAB4DE6EA48416AC86C2CCAE98298 /* Foundation.framework in Frameworks */, CB7893F1F8C12A415F474AAEA7360CA7 /* Realm.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -709,16 +734,16 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - A04BFC558D69E7DBB68023C80A9CFE4E /* Foundation.framework in Frameworks */, + 73B9C996AED49ED7CF8EC2A6F1738059 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - 9A459D8E0AC5F79ADD60D9F6F4B8F1CB /* Frameworks */ = { + BB669173F186BC19CAEC50D3F23D19F5 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 2F73A899F8F8DA9FFA99D3BBC709D585 /* Foundation.framework in Frameworks */, - 260A6263A4B8504601674415FB762CBE /* UIKit.framework in Frameworks */, + 779464C25D52447D04DD0A6C8A3CC40B /* Foundation.framework in Frameworks */, + C36F4DCD201581A1BBBAA6A911B8782A /* UIKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -726,38 +751,19 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 4EA3738D7B4B9ED5E012BA7A9F2CC382 /* Foundation.framework in Frameworks */, + F16B5A728A6D3560BB6766EDF4A5D087 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 159AE37EB87BC0A1BAE28FB6DDDAD1FD /* Pods-Anyway */ = { + 0313EBA70D230A3E0322F49B6D610C48 /* Resources */ = { isa = PBXGroup; children = ( - A12F78C2F68E04E316FE62951B25D169 /* Info.plist */, - 222C4BF8B3694CDFD93F89C630851F89 /* Pods-Anyway.modulemap */, - 46E6854A887E54294337DE1E1DC716B1 /* Pods-Anyway-acknowledgements.markdown */, - 27D9D9A0B31ECE9B436F57B6B414091C /* Pods-Anyway-acknowledgements.plist */, - F5203DAF180A9A8801705F411B711B01 /* Pods-Anyway-dummy.m */, - F98A06DF392EC6DD6830BF5AA4AF9442 /* Pods-Anyway-frameworks.sh */, - A86C68694ADEB912CBAE30E04992C31C /* Pods-Anyway-resources.sh */, - DC6C3BEF7CF735F64C406CEE2FE2170E /* Pods-Anyway-umbrella.h */, - 5A974C2BACAA1BC8BD9AB9085A77C421 /* Pods-Anyway.debug.xcconfig */, - A3E19E8BDBA82BE8168AE37FA9C1BF21 /* Pods-Anyway.release.xcconfig */, + F1F70F6236B9263466A90E34971C9A5F /* Eureka.bundle */, ); - name = "Pods-Anyway"; - path = "Target Support Files/Pods-Anyway"; - sourceTree = ""; - }; - 167B38B044F5ACDB16479EB116A225FB /* iOS */ = { - isa = PBXGroup; - children = ( - 7A3D9DA2C71CA686A1C42E7E25069931 /* Foundation.framework */, - BEF6D640145744CB8BEB33726BCB7547 /* UIKit.framework */, - ); - name = iOS; + name = Resources; sourceTree = ""; }; 265A16B789C8F72E9FC63FFCD7D8593A /* Products */ = { @@ -773,417 +779,449 @@ name = Products; sourceTree = ""; }; - 2DAFE460754C50C7D86814061F025911 /* Frameworks */ = { + 2801112E41FA60ABC5C1FEE11D0DA8B2 /* Alamofire */ = { isa = PBXGroup; children = ( - 44FF55BF977A4603AABEB5D2C38C1BE7 /* librealm-ios.a */, + FFB905F3B3B079C5759DDF3A00470EE4 /* AFError.swift */, + 2C0ED9FAB960CDD538A5C953B92F74C8 /* Alamofire.swift */, + ABA23A5018A81ACC4DC60114F737BDAA /* DispatchQueue+Alamofire.swift */, + F0664560BA9CC6CBF5068252B6986EE9 /* MultipartFormData.swift */, + 864B328D62AF03B02276BECCFDFF9040 /* NetworkReachabilityManager.swift */, + 296CB55358BAA7E6D7F3ED0162B610EF /* Notifications.swift */, + 986CA011E61CAC25F855201406097F66 /* ParameterEncoding.swift */, + F1CA9182A041E4565AB6FEA21C6D2421 /* Request.swift */, + 5A42EC471968E262A3E4B2D87D97EABC /* Response.swift */, + 6E9232B57FCBE99DC654345273A61422 /* ResponseSerialization.swift */, + 9B578B60A62261F86478ACD049E8E436 /* Result.swift */, + 097857138D9821D34C7FCB860A83C454 /* ServerTrustPolicy.swift */, + 53892895AD736DB83F24CB66C8BD5742 /* SessionDelegate.swift */, + 9458CF5DCA16A9827FA2C7D0DC6812FE /* SessionManager.swift */, + CE239D642911B6F39732B46D5AEC203E /* TaskDelegate.swift */, + 2AB6D864DBC97BBB5C7C05F5540E404B /* Timeline.swift */, + B726C060B1B3118F74436CE2D554AB90 /* Validation.swift */, + FB596766B817B939BEB2262459E8F774 /* Support Files */, ); - name = Frameworks; + name = Alamofire; + path = Alamofire; sourceTree = ""; }; - 58B1AD444C87D506C8EDE80ED2CF2E74 /* RealmSwift */ = { + 39305457625D3681E7480ABBFFDA8476 /* RealmSwift */ = { isa = PBXGroup; children = ( - D71878C452FE04FD80E0D2BE0842646A /* Aliases.swift */, - B07454B0F2C1553B654398EC391025D6 /* Error.swift */, - BA2FDCFC499C20ED3E774D1316DC884E /* LinkingObjects.swift */, - 956AEA5CF04A5AB3EC1142C5720085C2 /* List.swift */, - 920C51B4A494C88164B00C22CD447B4B /* Migration.swift */, - 72C33C05F0CC7EEDF1F38905DDE92572 /* Object.swift */, - 1D3821CDA73B90CD3BD9B05955E5BC4C /* ObjectiveCSupport.swift */, - 86627A77089E844EBACD47C208071806 /* ObjectSchema.swift */, - 7C82B5E5218853D5FF1D99A144639C0C /* Optional.swift */, - 240340D83B39E2F6B35CF6BC4CFD164C /* Property.swift */, - 0325E7C7F5AC1AE712D7975B1699FFE5 /* Realm.swift */, - E665E84C8E4BB0E3803FB153ADF872E7 /* RealmCollection.swift */, - C89A08A820500A904B6233379A9817E7 /* RealmConfiguration.swift */, - 8D89CAA2E126C17A26758614230011E6 /* Results.swift */, - D19A6659DD24000C3DCCE171456D16F2 /* Schema.swift */, - 3E231FEB8719347E19C2093B5BB82875 /* SortDescriptor.swift */, - EEFB9FD1287C8247D8948A9FBCB2846A /* SwiftVersion.swift */, - 4C0039B9DDC2472BE35918784A90AA0B /* Sync.swift */, - 7DCBEA864CF823C46E898153B245D8E5 /* ThreadSafeReference.swift */, - 7CED07EB9BDAFA7879F0A30A5824B79D /* Util.swift */, - B7FF75ADDE1518E62491080302E3F8A4 /* Support Files */, + 800126034A32DA13013907A513953FE1 /* Aliases.swift */, + FF71C7FE31DAA56DDEF5093CEFFB2961 /* Error.swift */, + 1898E6460BE4062611AF6E8820C8E0BA /* LinkingObjects.swift */, + 0EEAD778E47291DC5CA392CEAD168A93 /* List.swift */, + 62C933F380DD2F897B5294CD6BC830C9 /* Migration.swift */, + 28F0C763A6216AC9BEF1488A3FF128B6 /* Object.swift */, + 10E3D436E5AC2DC7953C3BF2822B8F69 /* ObjectiveCSupport.swift */, + 6C8B8BB4B7023504359EB1746965A799 /* ObjectSchema.swift */, + E6D52004D92F7AF737CB6373645DB781 /* Optional.swift */, + 88A609AD0DADD55736F3321FBE23117E /* Property.swift */, + B352EAE0150B12A4EBDA9B7420273246 /* Realm.swift */, + 79FAFB0B673F3C800E0154FDE6F2BF79 /* RealmCollection.swift */, + 5148E4A64F089541BBC2DDEDFC60CBD7 /* RealmConfiguration.swift */, + 8438FFD5AE38CCAE72808A74F062ECC8 /* Results.swift */, + 0F719281B4D3F905F5A67612C4BA14A2 /* Schema.swift */, + AF71550B6E0D71EE6264D5EFB88B3B16 /* SortDescriptor.swift */, + B06D8074680CFCAEF9159D23514BC4AE /* SwiftVersion.swift */, + 03AA6506C5ACCF14F540BDFAFF02204F /* Sync.swift */, + 0447FED55AE17952ABB23A6B30DE55AB /* ThreadSafeReference.swift */, + 8C81565305966867BC29F784D4A855B6 /* Util.swift */, + 3B0582DBC5AA876CF928A93C434DD4BE /* Support Files */, ); name = RealmSwift; path = RealmSwift; sourceTree = ""; }; - 5FDCCB0007F1E4C78CC4497DD13CE2F9 /* Realm */ = { + 3B0582DBC5AA876CF928A93C434DD4BE /* Support Files */ = { isa = PBXGroup; children = ( - 13E8AC0A040A95EE5A90B6279F2FD01B /* collection_change_builder.cpp */, - 9E4FF1360A4607377FBB5B1B3AFEEA7A /* collection_notifications.cpp */, - EB23711C1CEC6CF2337C5819D60BF159 /* collection_notifier.cpp */, - 4C7C80E15B5DE9055E1FB944495DC8BB /* external_commit_helper.cpp */, - 3FFBCFC6554676810CEBD0FB23414331 /* format.cpp */, - 619BF1FD92B3D6FE97860B2CBF9229C2 /* index_set.cpp */, - D3B238037C8AC4D64934FAEBE88C61DA /* keychain_helper.cpp */, - 2CC97A6737F1714F9EF84DDE43475C07 /* list.cpp */, - 00BD4125040D26E6F494F4A6BD624460 /* list_notifier.cpp */, - 0628BD4AB36BFDD0F70F5E1DB1DBF67A /* NSError+RLMSync.m */, - 5C41146D58D9108139484E4619940C2E /* object.cpp */, - 894692C9AB9605E81E9C6D693B2CDEF7 /* object_notifier.cpp */, - E9A4F74F141D09D7CD5998FAA9A1870D /* object_schema.cpp */, - D824F4C8D4EFE3AEE8781A238BF06950 /* object_store.cpp */, - 393D848359E7D9EA8202A481BE76DB05 /* placeholder.cpp */, - 15DB1C10C2C45C61470E5FDAD410FADF /* realm_coordinator.cpp */, - 1E5E1F7504471A7DC6A681EC7E71A87D /* results.cpp */, - 842AFED9641BA042831DB7383E12A2A0 /* results_notifier.cpp */, - 27C14377DF2A3FBB3817B4C9B050AB7F /* RLMAccessor.h */, - BE393BF220453660A254404E054F0FAD /* RLMAccessor.mm */, - 686701257DF37DBC70A18137BAD60B7D /* RLMAnalytics.mm */, - 846820F180FF593D8B30F33F453BF0B8 /* RLMArray.mm */, - 0E9E7653EE6E11A9D714A5FBD6F5698E /* RLMArray_Private.h */, - 508004E59A84C9E2BD7545539AB2B89A /* RLMArrayLinkView.mm */, - 11964F2393D2D4030E37AF622F8F7DF8 /* RLMAuthResponseModel.m */, - C08AC3525CE6AE6E9A38F18D070EDF56 /* RLMClassInfo.mm */, - EF964A1E8476F05A9A9F4A4D6259A1DD /* RLMCollection.mm */, - A7E633301E8BB0C26474AAA924E84975 /* RLMConstants.m */, - A1FABEEC135187CA74F85B3F768148C3 /* RLMListBase.h */, - ACEE2EB466686DFBBEF9A8D83DDF4BB7 /* RLMListBase.mm */, - D4CDCDC351D38138ECCED267801C5CBF /* RLMMigration.mm */, - 75DF55B3A46FC49075AE4BD6C9EA383E /* RLMMigration_Private.h */, - 2F550759D3307B38E90A1D9799D000DF /* RLMNetworkClient.m */, - A2E62568C016C9FD6DA9E44A207C7B9C /* RLMObject.mm */, - 08FA7D650AA736A88B61D2537CB3CD51 /* RLMObject_Private.h */, - 270E75F85B71197FEACBC19FBE748F37 /* RLMObjectBase.mm */, - 467FB620E0DFEF99D2F491E6D28CD4E5 /* RLMObjectSchema.mm */, - EB338157B00F36FB6060E70B536BDF66 /* RLMObjectSchema_Private.h */, - 7216CA306EE64A30BA3F7624E146E93D /* RLMObjectStore.h */, - CB30E2CDA387263CFF9EA9C05C5218E3 /* RLMObjectStore.mm */, - 1A18C31291781314DD00F7C5EB52F56F /* RLMObservation.mm */, - CE35665E296651539A94F942B81F95EE /* RLMOptionalBase.h */, - 11BE0F9CF79C0D969D2CDF3F990A34DA /* RLMOptionalBase.mm */, - 84471FEA32F71EB36161B5F2954E43B0 /* RLMPredicateUtil.mm */, - 43AB36B77AF38537BD827526EF2B7E97 /* RLMProperty.mm */, - A553DF821B37979C37BC0E3263C3F6A8 /* RLMProperty_Private.h */, - EF7DE4D98DFB45FBCEAA827C87595DE6 /* RLMQueryUtil.mm */, - BE87BD78CE2474F4AA9F7DDF39CC8A79 /* RLMRealm.mm */, - 60642B9E5F01ED218D76A28301764A6F /* RLMRealm_Private.h */, - 64B6D07C5164ED664F551EBD8B081CE7 /* RLMRealmConfiguration.mm */, - 882F164A5D289B372807FDDBD1A1CB62 /* RLMRealmConfiguration+Sync.mm */, - 24CA718B9D06B3DE1F62BB0C374BE311 /* RLMRealmConfiguration_Private.h */, - FA25F698977EFDD9705EDE8D7263E3B1 /* RLMRealmUtil.mm */, - 2295D12C85920E4B1E3F7706468DD794 /* RLMResults.mm */, - 4864FA14696A7225D14E5BAC8F7F448D /* RLMResults_Private.h */, - 62F10F059C31B04CCD3DBB715790FADC /* RLMSchema.mm */, - A9C3ECEAF6DD8DD56BC4C3B6BBF571B5 /* RLMSchema_Private.h */, - 5A0DD02D17A83F4D2E50F8FCFE01B983 /* RLMSwiftSupport.m */, - 7FEF75D28C51DD2C5CB7D482BBC916EA /* RLMSyncConfiguration.mm */, - CE12DBCCD12A2606CBE168BC27DAEEFF /* RLMSyncConfiguration_Private.h */, - F90EDF64FEF32A1E26029BB85ED1C56E /* RLMSyncCredentials.m */, - D6D779DD4034A34EB92387F98B71760D /* RLMSyncErrorResponseModel.m */, - EEEAC4F001764E87237EFCC8BC4A705C /* RLMSyncManager.mm */, - 505EB98642A13AD8D2015D0E58D9181F /* RLMSyncManager_Private.h */, - 7BFBD5CE655281E8C717BADD52E4958A /* RLMSyncPermissionChange.m */, - 2E3B856EDE75C56D20E19B60DA7C900D /* RLMSyncPermissionChange_Private.h */, - 92549DA3A81A89CD2DCD9EDF70CE9D85 /* RLMSyncPermissionOffer.m */, - 8E513C73F59953C62AFE3B41B410F108 /* RLMSyncPermissionOffer_Private.h */, - E829C2D738568B2DD7488FB084BD4EC1 /* RLMSyncPermissionOfferResponse.m */, - 0674E972D05DC625A513B8892778003F /* RLMSyncPermissionOfferResponse_Private.h */, - 199A99A47685911D68DE2E3BFB8468EC /* RLMSyncSession.mm */, - 3431B2BF741F061F001CE09129307498 /* RLMSyncSessionRefreshHandle.mm */, - BF3CF670586246BAEC381F6EB258D7C1 /* RLMSyncUser.mm */, - 12D42F13EF40C8807F1B51A7E183C2A6 /* RLMSyncUtil.mm */, - 6C05CDAFA31C3E20FFA2BA6425E1CEBF /* RLMSyncUtil_Private.h */, - 9E5E772C1B03083A40994D3FD70D5426 /* RLMThreadSafeReference.mm */, - F633BCB79FCBA0377A53C7E63ED27D02 /* RLMTokenModels.m */, - BEE43652C132B102F2A932B2491BD544 /* RLMUpdateChecker.mm */, - 3849234A5CFEC796842D8A0FF2EEAAE4 /* RLMUtil.mm */, - 5BEC800BBADAA10F12A2907AD52489C8 /* schema.cpp */, - 1D4BCE97A71DE1CDE56EEF209B6FE7CF /* shared_realm.cpp */, - 2719B5CBD57B584D6C0A839CFCC22254 /* sync_file.cpp */, - A2D088C2DCE81B9C32CAFE3F16D7003F /* sync_manager.cpp */, - 9F59515DA257EEB12738DD612E21D316 /* sync_metadata.cpp */, - 1CDBF32EDB2629D7DC97D7ED60FAFB32 /* sync_session.cpp */, - B3C815EEAB1A46D4EB01E54A27B22C0A /* sync_user.cpp */, - CFFD4E41A1EF23F20AAD44BC3F359A6A /* thread_safe_reference.cpp */, - DDA629004C61F9A1F688221E581BD35C /* transact_log_handler.cpp */, - 4E4498D0A2C0E7E34E8068D4254AECF6 /* weak_realm_notifier.cpp */, - 2DAFE460754C50C7D86814061F025911 /* Frameworks */, - 69B8C4CBDE3B1030CA59372B51A8DD8F /* Headers */, - 601233AAF4D99A4F580BAE840BBFD3F9 /* Support Files */, + 5DF7BA853685394814667C72C4229105 /* Info.plist */, + 6D9A0771F7C185DD785291A0CF832D3A /* RealmSwift.modulemap */, + EB9446C2B4219C267A6A53A4A1FA6017 /* RealmSwift.xcconfig */, + 3D114CB4B4F86FEAD7367E0C96B5C20E /* RealmSwift-dummy.m */, + 31A4A4B5B08D1395E2F90F18F0F1DD24 /* RealmSwift-prefix.pch */, + 176E8EC2BE2AEC13B33AED131EFC8FC1 /* RealmSwift-umbrella.h */, ); - name = Realm; - path = Realm; + name = "Support Files"; + path = "../Target Support Files/RealmSwift"; sourceTree = ""; }; - 601233AAF4D99A4F580BAE840BBFD3F9 /* Support Files */ = { + 505532C71255810A79E6554E97840076 /* Eureka */ = { isa = PBXGroup; children = ( - 136A067D3B0AD5F09C9BD5EE82CAEBE8 /* Info.plist */, - 47F175DC5CF5301F4BDBC428D5DF65BC /* Realm.modulemap */, - 2B46C6700BBE28BFE8DE7287BD5F560B /* Realm.xcconfig */, - B46433D888982EFBB459A99558D4D810 /* Realm-dummy.m */, - 552761D4035022EF8D5177C8CBF20E74 /* Realm-prefix.pch */, + F5FD6F5D01BF40B3873D41F079FE5595 /* ActionSheetRow.swift */, + 187B02672F278FDAB7F49040F27FF40D /* AlertOptionsRow.swift */, + A0F0AF7305F387DC1EB5B808BFDB0C0B /* AlertRow.swift */, + 78DE9922E322A9120D18498BFF31B499 /* BaseRow.swift */, + 126549DC194D6BB04E1151C548284B8D /* ButtonRow.swift */, + BC703D03A42D8D2D25BCE9D7CE1DC68D /* ButtonRowWithPresent.swift */, + 59EE81AEC41C45CC0D5D8D10B5E2FE14 /* Cell.swift */, + 31FD96ACD62FF946B63DD498461E60A2 /* CellType.swift */, + FAC63F855BA43317E954BD3FE6CA57B9 /* CheckRow.swift */, + AA691D57062AA682DE09170C69A1406D /* Core.swift */, + 5E81A5B9FF8E4836044E1AB76D5B216C /* DateFieldRow.swift */, + 43F37F19567EFD7F2634E392C4938D43 /* DateInlineFieldRow.swift */, + E4BE8DAE4F5784F3878092A687D0F7FE /* DateInlineRow.swift */, + FA0B007065C889BDFFC676FEFBB11F6F /* DatePickerRow.swift */, + 93D93C3ED858076114B56C5751004996 /* DateRow.swift */, + A22A60BC550E9C333E9F1B38A5505AA9 /* DecimalFormatter.swift */, + 00A48D5F9653E688D856A41C12A94455 /* DoublePickerInputRow.swift */, + 25B113DF62FF81079C8BFDA88738D5B5 /* DoublePickerRow.swift */, + 2819BE1610A8748120C720DC3AA0C66B /* FieldRow.swift */, + AF9E9D7F40ED5AAC52781E5915DFB4B9 /* FieldsRow.swift */, + 3D661C888B638570C004F215A0184808 /* Form.swift */, + EA6F7A4BEB49F3A40F3463280C8AA5FD /* GenericMultipleSelectorRow.swift */, + 7B5212E251572BA3D8FEF34549E8259A /* HeaderFooterView.swift */, + 523DBDBC9549556983C47F1BC098034B /* Helpers.swift */, + B7634052748874DCB824BA021EAC0031 /* InlineRowType.swift */, + 6FD0A44DB5FB29D4B787E94698D3504A /* LabelRow.swift */, + E99EA87D64192E461F9D10E54BA19CEF /* ListCheckRow.swift */, + E59898BDF46BA9F2CBE2DF02E4B92D42 /* MultipleSelectorRow.swift */, + 751759ECB55AEBE77F10C2ACBB5FC5C7 /* MultipleSelectorViewController.swift */, + 6ED883282453F89464A3E2B47B0D90A6 /* NavigationAccessoryView.swift */, + 6E0F53489BE5523FA7EC41E425BE6733 /* Operators.swift */, + DD6F999DFB1EB5A441AF482A5FC81007 /* OptionsRow.swift */, + 51D319F3644A3B5368DF418F82C60851 /* PickerInlineRow.swift */, + 40DC828D14E27D6671A3F0DF8EFA3FEC /* PickerInputRow.swift */, + E0BF74DA8204936480F9E9798F1CA3BC /* PickerRow.swift */, + 4214080B2BE7243648C4456549CE8182 /* PopoverSelectorRow.swift */, + C28F3F6AA1DB3A5174EE5A62B1F2A3CD /* PresenterRowType.swift */, + 45816D1BAA763492080FC611A65F04C9 /* Protocols.swift */, + 9D4C701C8D05720E98DE9B454811B7C4 /* PushRow.swift */, + 1DE5C10D442C8A56503D53FC9EFE1384 /* Row.swift */, + 7CEEBEA40DD326F9EE6940AD39294427 /* RowControllerType.swift */, + BBFCD450C7395E074EBFDD409E511993 /* RowProtocols.swift */, + 0947500D02EC4BF07CCC37BED5C229EF /* RowType.swift */, + BE22EFE05EAF99DD2057C42C33758987 /* RuleClosure.swift */, + 1203D759E1683FCE548BB3DDD477B8BE /* RuleEmail.swift */, + 1E205473F66611EB373B5AD7DF083FDE /* RuleEqualsToRow.swift */, + 7C59C69F2C46DB368087816142770B5E /* RuleLength.swift */, + 2E6B269162560363B382DF5BC7EF1C10 /* RuleRange.swift */, + F2D46A47CEAE656BFDBD8C38050C1226 /* RuleRegExp.swift */, + 26E6FB2127BE3D720B17AFD1B323E0C0 /* RuleRequired.swift */, + 14C503A15D13A17BFC554427E7F9A28F /* RuleURL.swift */, + D6A6499C4A8C3B77D08A8CC89C3A2CD9 /* Section.swift */, + FBDB3F8F7215ED51DA8A86CF19D1AA9C /* SegmentedRow.swift */, + CA74C2E8C385452EE0A250F6225371B0 /* SelectableRowType.swift */, + B01986AB41A7D9DFFB3AC8DF744D2008 /* SelectableSection.swift */, + 2F6043E48DDC0ABA3B90D64A62B848BB /* SelectorAlertController.swift */, + 20975C0C353901A50CCED023965AF70A /* SelectorRow.swift */, + E820F2BE88277EBA52781D57A928B924 /* SelectorViewController.swift */, + 55BF6C9C3259F218D33485F16A52709C /* SliderRow.swift */, + 2D2FC371C278EA6E7577D95C098247BC /* StepperRow.swift */, + 93E450EF6884F59FE08FF8AF0362C88C /* SwipeActions.swift */, + 95EF6D5795A0383AB2DC902E386A774B /* SwitchRow.swift */, + 51DD1B1FA32059B7169BC128A060A5BC /* TextAreaRow.swift */, + BF9BFF632E3DD6218BC5959537D0763A /* TriplePickerInputRow.swift */, + 64CE84F06C000C1E7D1BAAE4FA21C03E /* TriplePickerRow.swift */, + 4A7F0A0B8DFBE77352F497BE3B698066 /* Validation.swift */, + 0313EBA70D230A3E0322F49B6D610C48 /* Resources */, + BC25752919CA7BA061DB61C26AE471F4 /* Support Files */, ); - name = "Support Files"; - path = "../Target Support Files/Realm"; + name = Eureka; + path = Eureka; sourceTree = ""; }; - 69B8C4CBDE3B1030CA59372B51A8DD8F /* Headers */ = { + 51EC3D3307378BF4FA6281D7F5160B03 /* iOS */ = { isa = PBXGroup; children = ( - 8241C2625DF4891B7FC2A4445198A6CB /* NSError+RLMSync.h */, - E4CAF02FDE199C9936F3AAB9F5E80BA8 /* Realm.h */, - 5783296C13B6AB8D723ED137E1D96506 /* RLMArray.h */, - 57B66886E0B5A2FF762539B9888E7D31 /* RLMCollection.h */, - AC10336CC1E93B42D3F920ED569F5259 /* RLMConstants.h */, - 0F163D3E628B2C27B1508E055FCBB4FE /* RLMMigration.h */, - C0728F2E5AE5F61D049A15932F78BA19 /* RLMObject.h */, - 7EAA673441B602B9A00FF426FBD36DA0 /* RLMObjectBase.h */, - 32D27D06530CC7C698C09158E97B1714 /* RLMObjectBase_Dynamic.h */, - FDBD80532A14DC83B71C9D9D1B548103 /* RLMObjectSchema.h */, - D2A9F3F21B7819EF65B2FF6F110AF533 /* RLMPlatform.h */, - 10BBAB9E19BCC36E9E4EBF8BBF70EDEF /* RLMProperty.h */, - 1729FA2C3E2386CDE39B9777073DCDC3 /* RLMRealm.h */, - 4138285A9AFDCD8EA8A861545BAED78F /* RLMRealm_Dynamic.h */, - 8441212AE3E623321A15CF9A2931A5E4 /* RLMRealmConfiguration.h */, - 11A77345025CB3D7B627A351D33D7F5A /* RLMRealmConfiguration+Sync.h */, - 18C1C43D04AF90570D89DF2922616B6E /* RLMResults.h */, - C9F4EAB7ADB9F943D14DC790F2116D94 /* RLMSchema.h */, - 4527BBFB0222D834AEC2504878D0E941 /* RLMSyncConfiguration.h */, - 6039B922F638860B9FC3874C915E0671 /* RLMSyncCredentials.h */, - 834DAC5B97D31C4295577526C26859DD /* RLMSyncManager.h */, - DD4813B305768312DFCC46E17B165E9D /* RLMSyncPermissionChange.h */, - 9905EB2DBE88423775498C0762231D31 /* RLMSyncPermissionOffer.h */, - D5ED347940A6390F828FAA476C837545 /* RLMSyncPermissionOfferResponse.h */, - 0981BEFA01FEAE538C1510758D563982 /* RLMSyncSession.h */, - 502995188A080E6D6436BF64DAC9E5B4 /* RLMSyncUser.h */, - FEA2ED6C6EA03296AF7255E16C77C90B /* RLMSyncUtil.h */, - 38AA4F2C6E7EAC364B55224D98763F0B /* RLMThreadSafeReference.h */, + 14D0A18795829341CE4B7A3C6A1EEEB0 /* Foundation.framework */, + B8CDAC6FCDFA06F5D625331F21F5F7FB /* Security.framework */, + 3A3E10A84830BB961553A10D85557C35 /* UIKit.framework */, ); - name = Headers; + name = iOS; sourceTree = ""; }; - 7DB346D0F39D3F0E887471402A8071AB = { + 52490FF226913CDFF7C148E0FE2EEE4F /* Pods */ = { isa = PBXGroup; children = ( - 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */, - FA14DF3D743CD2DA584D1DBE06CA163D /* Frameworks */, - 96A084F888FE80BEEAB2A3A70DE0BC43 /* Pods */, - 265A16B789C8F72E9FC63FFCD7D8593A /* Products */, - CE63AFFA15CF231389F8D331D7024D58 /* Targets Support Files */, + 2801112E41FA60ABC5C1FEE11D0DA8B2 /* Alamofire */, + 505532C71255810A79E6554E97840076 /* Eureka */, + 89B63A619B39CCDDDCA1186134AF6548 /* Realm */, + 39305457625D3681E7480ABBFFDA8476 /* RealmSwift */, + B9951DE28563B84A02FEA8BD8C030959 /* SwiftyJSON */, ); + name = Pods; sourceTree = ""; }; - 871CABFC4BD398FC6D3D20C03C16EC02 /* Support Files */ = { + 59534380ED5CF727E062005C6BA3E475 /* Pods-Anyway */ = { isa = PBXGroup; children = ( - 48C69975462872C6FA68F38E77619C9C /* Eureka.modulemap */, - 86382517D56D223CE543C3F1067B3358 /* Eureka.xcconfig */, - F52E1A34E1E348A3265431E4106847D9 /* Eureka-dummy.m */, - 6C553278AD96C3EB670F12331FDD74B6 /* Eureka-prefix.pch */, - 66460A48ED0E8EB446519BCC5A09057E /* Eureka-umbrella.h */, - D4D7ECFF3A64CC20590DFD4824E6DFC4 /* Info.plist */, + C95C068012D70E696D3A2ADBBDB27E35 /* Info.plist */, + CA44D018B045CD243BEC0940E37ED73B /* Pods-Anyway.modulemap */, + A7B22BDEBAE7E859384071ABBC95CAA2 /* Pods-Anyway-acknowledgements.markdown */, + 6DA2151BB06A37D425D00DA1EBA6E7E8 /* Pods-Anyway-acknowledgements.plist */, + 4CA9E23211534D85FD7AC2B114442295 /* Pods-Anyway-dummy.m */, + F89F58C07805D6959B4FDC1B5212344F /* Pods-Anyway-frameworks.sh */, + 0523C02E11706E1C18914D46C313EB27 /* Pods-Anyway-resources.sh */, + C84FD9C200D57EE2834B9EF29FA80553 /* Pods-Anyway-umbrella.h */, + A0DA0FEA1E28CCA7CC5072098C38DC6F /* Pods-Anyway.debug.xcconfig */, + FF5A889E69302012309706AA00CEA87E /* Pods-Anyway.release.xcconfig */, ); - name = "Support Files"; - path = "../Target Support Files/Eureka"; + name = "Pods-Anyway"; + path = "Target Support Files/Pods-Anyway"; sourceTree = ""; }; - 96A084F888FE80BEEAB2A3A70DE0BC43 /* Pods */ = { + 6C714E4BED80B2F82A6DA8A26A88C74F /* Frameworks */ = { isa = PBXGroup; children = ( - B2BCBE38DD3886CD5141F1F2C75616A2 /* Alamofire */, - D78801B53F41CADD237BFA753CC2A4A9 /* Eureka */, - 5FDCCB0007F1E4C78CC4497DD13CE2F9 /* Realm */, - 58B1AD444C87D506C8EDE80ED2CF2E74 /* RealmSwift */, - E9B17640D976FE6C5CB46A1FF373886F /* SwiftyJSON */, + 246EB3837726573E0FFCC0A9481009D3 /* Realm.framework */, + 51EC3D3307378BF4FA6281D7F5160B03 /* iOS */, ); - name = Pods; + name = Frameworks; sourceTree = ""; }; - A89BCF1C3817DCA981DCA1067775C36A /* Support Files */ = { + 796CC984193DB435222140B2107F38FA /* Frameworks */ = { isa = PBXGroup; children = ( - 95F08EA278D9F6361DEB3006F384C389 /* Alamofire.modulemap */, - FFF83427A564921CED22CDEB18ECCD69 /* Alamofire.xcconfig */, - 5984CBF88D06136A0A23DE62752A4CB7 /* Alamofire-dummy.m */, - A57A6AE790A439930CD0BB306CBFAEF7 /* Alamofire-prefix.pch */, - B011D58D42FE700BD7FBE2FA010DA61B /* Alamofire-umbrella.h */, - CB78819040B9EC5BA9828CEA7B9C979C /* Info.plist */, + A406C04056749BE399A669BAD6983298 /* librealmcore-ios.a */, ); - name = "Support Files"; - path = "../Target Support Files/Alamofire"; + name = Frameworks; sourceTree = ""; }; - B2BCBE38DD3886CD5141F1F2C75616A2 /* Alamofire */ = { + 7DB346D0F39D3F0E887471402A8071AB = { isa = PBXGroup; children = ( - E06D431FD333C8EA06E4F2DF70DA7D76 /* AFError.swift */, - C83BDBC64DA966D95C5E7985BF558A63 /* Alamofire.swift */, - 5064684CC6FE7B593161C2C62A7BDFB1 /* DispatchQueue+Alamofire.swift */, - 386DCD377B9E54E299E4F8CEC8FF48E3 /* MultipartFormData.swift */, - 5FEC3405757873883235969FC527F2CE /* NetworkReachabilityManager.swift */, - B7EA768DC3032A622F5542785D38139F /* Notifications.swift */, - 84DD011B908DA95F09D9D14D38BA0249 /* ParameterEncoding.swift */, - A1A043D377AAD1E52073686C96FF4CB6 /* Request.swift */, - 6952E45E49E7F662E8E8F9B51CA18863 /* Response.swift */, - C1FA3F092353477F953E1F680C029226 /* ResponseSerialization.swift */, - 407F9684B0240170F95F9A6F7E844E68 /* Result.swift */, - 5B625B7E1AE15C871934DC51A58AB73E /* ServerTrustPolicy.swift */, - 7311A2BBD138BA02D3D1AFEB8009F42F /* SessionDelegate.swift */, - B8DC206D5ABFFD9DCC07EA5B9A28D151 /* SessionManager.swift */, - B4A4FBE832D1BAFB8C77FA4082C6B113 /* TaskDelegate.swift */, - 59BF4F23201A68D17489D807C9E924B3 /* Timeline.swift */, - 686F5B1BAF09AE338CA46A328DE359FB /* Validation.swift */, - A89BCF1C3817DCA981DCA1067775C36A /* Support Files */, + 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */, + 6C714E4BED80B2F82A6DA8A26A88C74F /* Frameworks */, + 52490FF226913CDFF7C148E0FE2EEE4F /* Pods */, + 265A16B789C8F72E9FC63FFCD7D8593A /* Products */, + CE63AFFA15CF231389F8D331D7024D58 /* Targets Support Files */, ); - name = Alamofire; - path = Alamofire; sourceTree = ""; }; - B7FF75ADDE1518E62491080302E3F8A4 /* Support Files */ = { + 8972E4310E368C7BDE5BB602F511356F /* Support Files */ = { isa = PBXGroup; children = ( - 713BB555A898D021D9A96132F303A3D5 /* Info.plist */, - 28375129CD5D31DE7F372C9F31C3D655 /* RealmSwift.modulemap */, - F6A81900E2DEB7EFBC9DFAAD9B91F191 /* RealmSwift.xcconfig */, - B5AB4446CFFCD1BF3787BBC4D68654CB /* RealmSwift-dummy.m */, - 90AF4D3347901168470C6AE662FC9D13 /* RealmSwift-prefix.pch */, - 0BD5E7FE8D39F82606529218C8B9EE24 /* RealmSwift-umbrella.h */, + E577F23C05487C0A645ECB9920D9905C /* Info.plist */, + 4674073253B272A5C44AD0FE471E57A8 /* SwiftyJSON.modulemap */, + AA392BA26630759D689717E517366E41 /* SwiftyJSON.xcconfig */, + 30AE50683B795C951A63D332AD22223C /* SwiftyJSON-dummy.m */, + E4F426F9466D985EEFDD180245D48E94 /* SwiftyJSON-prefix.pch */, + B5DAAF083C1981C7AC6747B2D61DC84B /* SwiftyJSON-umbrella.h */, ); name = "Support Files"; - path = "../Target Support Files/RealmSwift"; + path = "../Target Support Files/SwiftyJSON"; sourceTree = ""; }; - CE63AFFA15CF231389F8D331D7024D58 /* Targets Support Files */ = { + 89B63A619B39CCDDDCA1186134AF6548 /* Realm */ = { isa = PBXGroup; children = ( - 159AE37EB87BC0A1BAE28FB6DDDAD1FD /* Pods-Anyway */, + D1B7F7FE8842DA2A2915B46BA27D96EF /* binding_callback_thread_observer.cpp */, + 6C8D84EC7A695F0DFDA1DD6F69A82FE0 /* collection_change_builder.cpp */, + 1B0270EC4A80A78F37460EDA7A9EB51E /* collection_notifications.cpp */, + D1091D628F3A1E41BA2BCD0AAD0190A3 /* collection_notifier.cpp */, + DB06C6D64D9A14CABD9501EF856F74C3 /* external_commit_helper.cpp */, + 124D4BADF34C3392E8B1E56EF4F2A663 /* index_set.cpp */, + 7F525C17DDBB7FA46D49579145BDEDFD /* keychain_helper.cpp */, + 6024BE960393D401008CED073BF041D5 /* list.cpp */, + 194A2BE505076878605C44E278887A41 /* list_notifier.cpp */, + 586218F9863E99A2078D4466D65A01D2 /* network_reachability_observer.cpp */, + 3318D76ECFB236EFC0BE76A4FB42F14C /* NSError+RLMSync.m */, + EE7100D4160D470F3B510553F389469F /* object.cpp */, + 24E2229A2BA4D575C6E5A758F693A40F /* object_notifier.cpp */, + D6AAF42155764E91D966EFE018D4F18A /* object_schema.cpp */, + DBB517B3130AECB9E6AC13567F48BC38 /* object_store.cpp */, + 327C13A5E90EFC1A5BF2C497A34D014A /* partial_sync.cpp */, + 032E3F03CAA28A8D926A754A61106180 /* placeholder.cpp */, + A23EF01AB8B863A114F893F1DE53FA9E /* primitive_list_notifier.cpp */, + E40D41B07E90F49DF435B9EB8603D8E8 /* realm_coordinator.cpp */, + 40586DDC9DF702A45F5EBEC431419383 /* results.cpp */, + 529D2CCFB71D0697A0F9F093C4D6FAEA /* results_notifier.cpp */, + 64D8D0B21B9ACFD54B1A199C25EB73AB /* RLMAccessor.h */, + DB7795DEDC3688BE80EBE0633A325028 /* RLMAccessor.mm */, + 04002741C9DC9598E4522E5C7784BE74 /* RLMAnalytics.mm */, + BB69FC881A589171D4EA27D5D89DBAF3 /* RLMArray.mm */, + EA80AE3316691FF6213775C016E85D07 /* RLMArray_Private.h */, + CE4D9A2CB050B8F48137F0CB588079D7 /* RLMClassInfo.mm */, + D386F72C81BB35F7759273D64401BA4E /* RLMCollection.mm */, + D710B0CC80C2FDA501FD4F5D2FC52B2D /* RLMCollection_Private.h */, + A7BCA7A83702D9E86CDA34631864173C /* RLMConstants.m */, + 6F4BF5CD03006FF03F3A27B25DC0F87C /* RLMJSONModels.m */, + 91D870DD127FCCA184837D3AA0A3F503 /* RLMListBase.h */, + 5D58DDDAF8A6088DF40E48B6DB866494 /* RLMListBase.mm */, + 6649B98D9AD056DE4CFFA3D8A37F8925 /* RLMManagedArray.mm */, + E2DF58383A66F988377F979F6E35039D /* RLMMigration.mm */, + BFC0EB7C7CE270DD215E17D9351C311A /* RLMMigration_Private.h */, + B6D34AA8D31CDEF7005ED43E1DE58E5D /* RLMNetworkClient.mm */, + D59D03A16268D980788997D03C5F8D89 /* RLMObject.mm */, + 1BE4DDB16CBE64F4A80116FA616B5E61 /* RLMObject_Private.h */, + 649F42D8A431E971B7E9584E04BC442C /* RLMObjectBase.mm */, + C3D5A37CD39E1E15FB43F26E2D9F7786 /* RLMObjectBase_Private.h */, + 702440DC1A628016068AF5189F2DC8C9 /* RLMObjectSchema.mm */, + AFA7FB6D6175EBF3AA8DDB331F9C8083 /* RLMObjectSchema_Private.h */, + C36443BD174010DA804B31FBA85C761D /* RLMObjectStore.h */, + C1A6B66006D194520803DE174BC1EC19 /* RLMObjectStore.mm */, + 25456002CE8D3E5F3359AF03BEA449D4 /* RLMObservation.mm */, + BB2B4D0F33C6541E253E580A9771D48B /* RLMOptionalBase.h */, + 8FEEFF42B6B8348C08E9D90871789F14 /* RLMOptionalBase.mm */, + 0BEEC4B884A769315B12C502799ED7D9 /* RLMPredicateUtil.mm */, + CD99ABACAA310323AEDC1AE7A9E65321 /* RLMProperty.mm */, + EEB08D83627669E22B8E0E7D16CDAFB1 /* RLMProperty_Private.h */, + 344A2CB7F04E410D131FD5DFDC3E5C40 /* RLMQueryUtil.mm */, + 9F51A7E1B01B2BB97ADBB863BD6D4302 /* RLMRealm.mm */, + 4BFE492D70CF60F74A461A4C34BA70F0 /* RLMRealm+Sync.mm */, + 28210C7A86CBE8162C3D55C22079D4B2 /* RLMRealm_Private.h */, + 001110A6560B90CAE1EB189F2495F8D1 /* RLMRealmConfiguration.mm */, + C98E314F648A32E9F16CCEF7FB450E16 /* RLMRealmConfiguration+Sync.mm */, + DE71E71F838918B77F921956DCAEAA0A /* RLMRealmConfiguration_Private.h */, + C55AD46B8ACAF57640C65CA2745B13BA /* RLMRealmUtil.mm */, + 3E40088A1D0BC7B2FEE5F5BEA221FF6F /* RLMResults.mm */, + FD6809E2C37CAAEF32341A838B340ED8 /* RLMResults_Private.h */, + BE2AD210BCB92444548FF2D08C23D017 /* RLMSchema.mm */, + 57C9F248C0AEC8E0A677F1A588EA291D /* RLMSchema_Private.h */, + 78D9249A3C56C111F67D87AAFBD4037E /* RLMSwiftSupport.m */, + 10EB8EE4D5BCC0493710CB96CC7CBA64 /* RLMSyncConfiguration.mm */, + BA0C8517B53BB218529BE33B0282E3B1 /* RLMSyncConfiguration_Private.h */, + 590D565903C5870CE8E04F9714DCADAF /* RLMSyncCredentials.m */, + 588E52B881F749AF8A57755A955AE4FC /* RLMSyncManager.mm */, + 1B57A881AACF13C810FE8799270BC239 /* RLMSyncManager_Private.h */, + 10450524A1F8D00FF064E5B7E2810EFD /* RLMSyncPermission.mm */, + 04E903ADF8F796BCAD79F12C0E094A5D /* RLMSyncPermissionResults.mm */, + B6979907A286F1FF396BC34194D33F73 /* RLMSyncSession.mm */, + 175A9E8928769E05D3AD017C36A4F8E2 /* RLMSyncSessionRefreshHandle.mm */, + 83A0A28CC02CD6AB04BB51F73F8CB169 /* RLMSyncSubscription.mm */, + 14E99727ACE0BB6DB0B22DB41EE62C2F /* RLMSyncUser.mm */, + F7B108A8D73BBF19C20D7D63650FDE14 /* RLMSyncUtil.mm */, + 2E6D45894756DCCFAF546F9C2EC4CC1A /* RLMSyncUtil_Private.h */, + 2CACB94AD762BB71C001B2762B43E660 /* RLMThreadSafeReference.mm */, + F7172D19BC41F018204B6397B905F0CE /* RLMUpdateChecker.mm */, + 113684CC953F62A508BFA81EC4AA2D22 /* RLMUtil.mm */, + 43198200D81D6B690D170883B3D376ED /* schema.cpp */, + C1447446CBA5E861394374981B4CE394 /* shared_realm.cpp */, + 87C392FB2A1558DFB70FDB1D384A0218 /* sync_config.cpp */, + 49AB68E08DFCC8BB5588683D51F70C42 /* sync_file.cpp */, + BE8FD25136279F7A06411526D1F4CEEF /* sync_manager.cpp */, + 7AB110AA2342D66D7CC1EC5DE3A9AF95 /* sync_metadata.cpp */, + ED2C32B64ED0E893C1E80242A025A8B6 /* sync_permission.cpp */, + 9486BD497E3820245DCFD08D07285B0A /* sync_session.cpp */, + 75871C8208172885ED1B2473CD69CFF9 /* sync_user.cpp */, + DC698CDA7D7633277D18E4E2DDFBECA8 /* system_configuration.cpp */, + D53168BD4DCE3E1733D2B567714F78D4 /* thread_safe_reference.cpp */, + 95645FF346EE5177388A4C1D971F81E4 /* transact_log_handler.cpp */, + 655EFBA9BCFB51B0DAEC75CC202C03CD /* uuid.cpp */, + 49A2B3436DB98A5B3255CDD8ABC660FC /* weak_realm_notifier.cpp */, + ED7F0E7D00FF705E8A813AE82E4FB94F /* work_queue.cpp */, + 796CC984193DB435222140B2107F38FA /* Frameworks */, + FD71DD9385FF3AA5D35601615ABB6DF7 /* Headers */, + D699A66BD7F18128D462BD11C6C1FFA1 /* Support Files */, ); - name = "Targets Support Files"; + name = Realm; + path = Realm; sourceTree = ""; }; - D78801B53F41CADD237BFA753CC2A4A9 /* Eureka */ = { + B9951DE28563B84A02FEA8BD8C030959 /* SwiftyJSON */ = { isa = PBXGroup; children = ( - 2D403206900FBDE3E3E8F9E470DC1D7C /* ActionSheetRow.swift */, - 6B0A8743C5F9B5685DF0FF5B8D162D8D /* AlertRow.swift */, - F7B32E1E31A1D58627A01759618B4D79 /* BaseRow.swift */, - EC3368D01A978261649708EACBBC2E61 /* ButtonRow.swift */, - C4F3E40991A37DCB4879656F07CC9B31 /* ButtonRowWithPresent.swift */, - 084D1115E70849A725D618FF67761945 /* Cell.swift */, - 4C65356B61466B850C0C42E1D84A3104 /* CellType.swift */, - 883CEA612E14A5F05417D6AD6B3297BF /* CheckRow.swift */, - 6F7E0ADF17EBC958B251815CC7568CDA /* Core.swift */, - 03C6CE1F1C11DD39FEB2B8EF746CCC4E /* DateFieldRow.swift */, - 6644A671463D040EAD5387939EB43DA6 /* DateInlineFieldRow.swift */, - D3646CE1E7000B6E5DACD713D0610F22 /* DateInlineRow.swift */, - CAD6BFE59B3D53D339C52EE758C9E1B6 /* DatePickerRow.swift */, - E3D815140621A399552C48BF97759F23 /* DateRow.swift */, - 1C3C9AC34A7A7C83578E7E6D2B8F0101 /* DecimalFormatter.swift */, - 17B7645C7D765D49975F13208959E78D /* FieldRow.swift */, - 04F8D2F7C2B72C1FE0F3F9CFCD347B9C /* FieldsRow.swift */, - D2EF20A785AA2BDD2D0C2BF1D987A6DB /* Form.swift */, - FC0C3EE90D3935E7766DDB5EF134852F /* GenericMultipleSelectorRow.swift */, - C7BC9AA4FF20F481CD6316CF967E7E44 /* HeaderFooterView.swift */, - 773791FAABE10D0CD841A405B8EBD833 /* Helpers.swift */, - 531746272E0DBC0075555120A675147D /* InlineRowType.swift */, - 2EFD0126BC7BBC68CDA40D17FF5FF701 /* LabelRow.swift */, - 93C123B1B31F0D8126DFA0BD859D2379 /* ListCheckRow.swift */, - D9B2A16F6F51E0E5E465E8DF9B960BB6 /* MultipleSelectorRow.swift */, - 36B2E6217FCAFD472E13DA1BFB06DD25 /* MultipleSelectorViewController.swift */, - 90123BC19B6698C2EA04510241E8A9F1 /* NavigationAccessoryView.swift */, - AF277B8952F3E92239B0266AEFEB566D /* Operators.swift */, - D127167FD3356138E35CD696019AFDAC /* OptionsRow.swift */, - 0D0589AF94C2F182098FF711C58BE901 /* PickerInlineRow.swift */, - DF96E6C5E79D7F0FFA45A108EE7E3CB6 /* PickerInputRow.swift */, - 2D0CE63E3233460D4B17968656664499 /* PickerRow.swift */, - 25E5133FA249E54193DCD306DB40A7A0 /* PopoverSelectorRow.swift */, - 6A284B16D14F93475C41DAE1873649B5 /* PresenterRowType.swift */, - CA60E1705211357CF6C402D21D847134 /* Protocols.swift */, - 5424BE0CC5F454C6457E5F589E917AEC /* PushRow.swift */, - F587B0BB1761780DA10D5C25BFAB5DDC /* Row.swift */, - BE125D3EAB2AD7398DA1E3D3A7267498 /* RowControllerType.swift */, - 3C59F0E01E7F300BE1CFDDECCB8180EF /* RowProtocols.swift */, - 520F99B6CFE5ED981E6BAC5559A35473 /* RowType.swift */, - 0FA9915B3202603A2CEE9969409231EC /* RuleClosure.swift */, - 978B783E5ACC1ACAE6353A5AB032D93F /* RuleEmail.swift */, - 373AB91A812DCAD2C3036DA8E3DEDFB6 /* RuleEqualsToRow.swift */, - FD09069DAF0A3339E871F4D51BE6BB73 /* RuleLength.swift */, - D53FB91EC150CA439DF95B39FE6543C6 /* RuleRange.swift */, - F84F232C37EF8A2DD0C9FF8DD9ADFC08 /* RuleRegExp.swift */, - 9AA29F62615D2D85B1E418BED094A73C /* RuleRequired.swift */, - 6D9BCF83EC8706B0D38C3F5CA6912FF3 /* RuleURL.swift */, - 03A6ADFB0A4503070DF6C5589F3F3E20 /* Section.swift */, - 0D950242A064EF6D387D762AC469B49A /* SegmentedRow.swift */, - 9659455EF6CAF526AD0F5AEEC11A5CB7 /* SelectableRowType.swift */, - AC6BDBD16312E5555C4A809908EE5F11 /* SelectableSection.swift */, - CB147AC31F137C05E67DC35CBB4EED2E /* SelectorAlertController.swift */, - 449CF09F2B27B4F97F08D2453AAC4DA6 /* SelectorRow.swift */, - 29D0734057FB701FB6B90FC6A17E9025 /* SelectorViewController.swift */, - 50FB6F40AB6BB889316E02C3F71DFFB1 /* SliderRow.swift */, - 8991107A2B7B547BA854EAB256F08781 /* StepperRow.swift */, - AAD4FBA1295E2A108DB8344445050359 /* SwitchRow.swift */, - F8225E11B668E56A71D602CB391CBF5C /* TextAreaRow.swift */, - DA6E5A8C807057AA6361F8C7A5700BFE /* Validation.swift */, - FE6C842B53F0BD241BA27D4A54E01B1A /* Resources */, - 871CABFC4BD398FC6D3D20C03C16EC02 /* Support Files */, + 83DB289513B9755814CCED9C686CFCED /* SwiftyJSON.swift */, + 8972E4310E368C7BDE5BB602F511356F /* Support Files */, ); - name = Eureka; - path = Eureka; + name = SwiftyJSON; + path = SwiftyJSON; sourceTree = ""; }; - E9B17640D976FE6C5CB46A1FF373886F /* SwiftyJSON */ = { + BC25752919CA7BA061DB61C26AE471F4 /* Support Files */ = { isa = PBXGroup; children = ( - BCFFE113430B44131E8A2FFE5C595B78 /* SwiftyJSON.swift */, - E9E98B2FA8E38B701F2F2BF523CAE02A /* Support Files */, + E0010B5FFEB587C83EA43BD6E57F6D9F /* Eureka.modulemap */, + 4DD427720AB8D7C10B9C2F9F6B691617 /* Eureka.xcconfig */, + 0488E4C9B99E1C7153BD52CF62BE6FF5 /* Eureka-dummy.m */, + 746A280B033147EDBAD7070E10EC72BC /* Eureka-prefix.pch */, + D36EF80003D71394190566A94C169AA0 /* Eureka-umbrella.h */, + 4E187DCAE0A137EF50EB8FDC44B52168 /* Info.plist */, ); - name = SwiftyJSON; - path = SwiftyJSON; + name = "Support Files"; + path = "../Target Support Files/Eureka"; sourceTree = ""; }; - E9E98B2FA8E38B701F2F2BF523CAE02A /* Support Files */ = { + CE63AFFA15CF231389F8D331D7024D58 /* Targets Support Files */ = { isa = PBXGroup; children = ( - 3E454138880D8212824C37378378EEF4 /* Info.plist */, - 68E9156099F1EB7EDE460D4E157BC6B4 /* SwiftyJSON.modulemap */, - 8436E1EDA6FAD0175D5D6728FA4DF0CA /* SwiftyJSON.xcconfig */, - A1C7E0F431B5E7F5D1489E545A37E07D /* SwiftyJSON-dummy.m */, - F1E602FA60F24443233E5EC8692D47B1 /* SwiftyJSON-prefix.pch */, - 8FC1C64BD2D33913F8DDD197764A345E /* SwiftyJSON-umbrella.h */, + 59534380ED5CF727E062005C6BA3E475 /* Pods-Anyway */, + ); + name = "Targets Support Files"; + sourceTree = ""; + }; + D699A66BD7F18128D462BD11C6C1FFA1 /* Support Files */ = { + isa = PBXGroup; + children = ( + 04102893CCCB7CD3F95033115D032B10 /* Info.plist */, + 61DBF1126B97DC2DC3232D511E978638 /* Realm.modulemap */, + 370D52E54F48A531CF8544EAD9E80C15 /* Realm.xcconfig */, + 11CA426804A9BB072D81CAA11AD1F09A /* Realm-dummy.m */, + 79941B3181D74379FD373C31B5C69947 /* Realm-prefix.pch */, ); name = "Support Files"; - path = "../Target Support Files/SwiftyJSON"; + path = "../Target Support Files/Realm"; sourceTree = ""; }; - FA14DF3D743CD2DA584D1DBE06CA163D /* Frameworks */ = { + FB596766B817B939BEB2262459E8F774 /* Support Files */ = { isa = PBXGroup; children = ( - 8D4A407D80D0F5AD849A44674551336F /* Realm.framework */, - 167B38B044F5ACDB16479EB116A225FB /* iOS */, + C78E593BBD7D760BCB9C41D3CF56CB69 /* Alamofire.modulemap */, + 43B756E9365921724D5897717226AE33 /* Alamofire.xcconfig */, + 45AB90ECD8AB5DD612FB49FDD020BAEE /* Alamofire-dummy.m */, + 8570192DA2887EFCB6C6912D70E66D9D /* Alamofire-prefix.pch */, + AA54227EA9DEF8A1F486F1ECE61992F0 /* Alamofire-umbrella.h */, + 49225B20499ADADDFE4F21C944855FA6 /* Info.plist */, ); - name = Frameworks; + name = "Support Files"; + path = "../Target Support Files/Alamofire"; sourceTree = ""; }; - FE6C842B53F0BD241BA27D4A54E01B1A /* Resources */ = { + FD71DD9385FF3AA5D35601615ABB6DF7 /* Headers */ = { isa = PBXGroup; children = ( - 1E13F8D90E94321D9AC567411EEAFEAB /* Eureka.bundle */, + B1DC356638CEC2013EB2AA5C48324795 /* NSError+RLMSync.h */, + 11CD0690F393036A8C39A6F395DF660E /* Realm.h */, + 477D374261C345967081120A29C9D12C /* RLMArray.h */, + 18EA699644630EFE9C3F737E1D7AA22A /* RLMCollection.h */, + E8A82C9D0BBFA86E32BCC4120D292EF3 /* RLMConstants.h */, + 0F620F20591BA62E035F169DE1CE3FE5 /* RLMMigration.h */, + B3886B0BB14B2EF5C4262922ECF3DE2E /* RLMObject.h */, + 6DC7EB8E3DE789DDA96E80E38F29D51B /* RLMObjectBase.h */, + CC33867D8B53A2679CF5D156B7132E9B /* RLMObjectBase_Dynamic.h */, + 84B59231ACC04A09E7261D7431571B3D /* RLMObjectSchema.h */, + 7A8920FC9084B4C9A81116734A9749A0 /* RLMPlatform.h */, + 924FEE607EDFD95455B17DDB02D0923C /* RLMProperty.h */, + EF7F18C6A90F1DD1BE53BCE4102A40B4 /* RLMRealm.h */, + F42A8422312B929411F5B93DEEEFA7F8 /* RLMRealm+Sync.h */, + 8EF03B0A1821B57A4D813FC44E6DA4DB /* RLMRealm_Dynamic.h */, + 558D13F22302404CE3C9B913877AE37A /* RLMRealmConfiguration.h */, + CB5C94E9B607AB4BB4B946482BFA6B8F /* RLMRealmConfiguration+Sync.h */, + 90DB17A7AFC7EB4978CF84E262E454D5 /* RLMResults.h */, + 15557EEAE9332FBEF162AC42204AB072 /* RLMSchema.h */, + 1D700D1CF120FEA45E20E220BBD986A1 /* RLMSyncConfiguration.h */, + 80209A2BD2DAA37387B2006851344114 /* RLMSyncCredentials.h */, + 03330053F888BD923BF4C4CD4C46800C /* RLMSyncManager.h */, + 55883507AB23386BC49FDAA28C3C368A /* RLMSyncPermission.h */, + 46420844EAA978DC4528A13BB970D63B /* RLMSyncSession.h */, + 5D1D46BBFB6D9B74339D10D0FC54F60F /* RLMSyncSubscription.h */, + 6FEABED8562208C6D084A3C7C1B5B913 /* RLMSyncUser.h */, + 4D7E4F5987926F2C2BC25B00EA185D7A /* RLMSyncUtil.h */, + 7A773B69EBB1239A6A18F2BB26781989 /* RLMThreadSafeReference.h */, ); - name = Resources; + name = Headers; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ - 1F412B9DD67AD97FE17C6F9513349CCA /* Headers */ = { + 708403E914230BFB53B3A055F1E2A38A /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 3F0D960DBEF20736A7A505A9250942F1 /* Eureka-umbrella.h in Headers */, + 9DD39D0018BD08D5A72089ADF663F85F /* SwiftyJSON-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; - 708403E914230BFB53B3A055F1E2A38A /* Headers */ = { + AE09BD9940CDDAECDD3E71594C156058 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 9DD39D0018BD08D5A72089ADF663F85F /* SwiftyJSON-umbrella.h in Headers */, + 24412725F72AB781C65FED663E5B25DE /* Eureka-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1195,57 +1233,56 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - CA4163637E6167E837291501FBFB9A2B /* Headers */ = { + D4319C3F3DD8A6825FEFEAB7E81F5AF1 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 9C3A69B8128A844F6EF3A158E7F07986 /* NSError+RLMSync.h in Headers */, - FD579EB9BB47796E30111D838FF01277 /* Realm.h in Headers */, - E1A9ED5C87F358716FADD70967790B46 /* RLMAccessor.h in Headers */, - FFBC51DD335C01104A67BDC34483EC8D /* RLMArray.h in Headers */, - 2F4DADD310C4D1D5A730DDFBEAF0B4A2 /* RLMArray_Private.h in Headers */, - E31AFF108FBD023D6CB081E257034CD8 /* RLMCollection.h in Headers */, - 184852455BA8F838459DBEBD6D6C8D84 /* RLMConstants.h in Headers */, - 847DFA085558797301022D6F2A769CD0 /* RLMListBase.h in Headers */, - B1DBE4FCD5C52290C5977E2B00554FEB /* RLMMigration.h in Headers */, - 6C827723C97288F55395DBAA76BC5219 /* RLMMigration_Private.h in Headers */, - C4CA79DE19A03ADC323D8B384A81FB4A /* RLMObject.h in Headers */, - C8DFFCF5E1FD9DB1C01640461EFC1460 /* RLMObject_Private.h in Headers */, - 49915E6633A6DD78BAC3F33AC716253B /* RLMObjectBase.h in Headers */, - AFD0AD548634699D7D1C318038122D52 /* RLMObjectBase_Dynamic.h in Headers */, - FF706EFA096ABE46C4BE087E54BABCDF /* RLMObjectSchema.h in Headers */, - CF1E8CE44F4C4104A21C7F7EA06F030F /* RLMObjectSchema_Private.h in Headers */, - 27A22C298CD655BDF0D016F9066CE5D4 /* RLMObjectStore.h in Headers */, - D68A349751A7850D73EF9D1CA1452C52 /* RLMOptionalBase.h in Headers */, - 6BEE4DE62BB8FF74379FD3214824ADB6 /* RLMPlatform.h in Headers */, - 6E9EEF9D90454CBD675A126B6057B1FF /* RLMProperty.h in Headers */, - 453B0BEA023B4C89CA3E3B110C72E9DD /* RLMProperty_Private.h in Headers */, - 600C400E08DD9EC2B3A70CA18919A3A4 /* RLMRealm.h in Headers */, - 2AAAEA56F136003D17E38437CB6EDBE1 /* RLMRealm_Dynamic.h in Headers */, - 4CD31C93C75FA5A1E98DACC14B3BD127 /* RLMRealm_Private.h in Headers */, - C2631DEBEF1988EFB007B6C15D526835 /* RLMRealmConfiguration+Sync.h in Headers */, - 2799D136D7C0ECF8FDF37CBBA678E44E /* RLMRealmConfiguration.h in Headers */, - 7EA353CB5698899BF205F84F3880969A /* RLMRealmConfiguration_Private.h in Headers */, - 1659B1265BBB42366D2413083AF0129A /* RLMResults.h in Headers */, - D64EEA42DA4A15A9CA19AB445465E0E0 /* RLMResults_Private.h in Headers */, - 3BFF9B9630C8E48FB861273FB4E00BC6 /* RLMSchema.h in Headers */, - 568B4EC01165E670833A947131AC1596 /* RLMSchema_Private.h in Headers */, - 26F4CE107C3719EAF451E3CBE774EC6F /* RLMSyncConfiguration.h in Headers */, - B68A1586D3548E912AC1D46EAD369611 /* RLMSyncConfiguration_Private.h in Headers */, - 4048A09CFF693AE1A860FB7BB9079CDE /* RLMSyncCredentials.h in Headers */, - A43E0EFA21B9CE76E7103E8A8C7EFBA3 /* RLMSyncManager.h in Headers */, - A290DFCED7C29574C0153583BF6C4BA3 /* RLMSyncManager_Private.h in Headers */, - 04292A24DCB888A874716AED5A0991B5 /* RLMSyncPermissionChange.h in Headers */, - C905D1D56E63424E065733EEFDF885B4 /* RLMSyncPermissionChange_Private.h in Headers */, - CC608E72A4E1D0E7F7FB9CB971FCF084 /* RLMSyncPermissionOffer.h in Headers */, - F6FBA9E3B0B2AD6527F8699B6352EE5F /* RLMSyncPermissionOffer_Private.h in Headers */, - 923E5D2F577BB94EEB395EA4D4F322AC /* RLMSyncPermissionOfferResponse.h in Headers */, - F9BA17EDA26513DA8F7BCAD3EC9DE885 /* RLMSyncPermissionOfferResponse_Private.h in Headers */, - 30B389B346A12D2E24E41F568891070F /* RLMSyncSession.h in Headers */, - 76336182ECAD0ACF61371D6F66D8F01E /* RLMSyncUser.h in Headers */, - CD9DEB0D28DFDC0ED1496CEDCDCE45FC /* RLMSyncUtil.h in Headers */, - 1727C54D0D94CE7C475585ED927DB6F1 /* RLMSyncUtil_Private.h in Headers */, - 824069A0AD3321FE296EF88D544CB3D9 /* RLMThreadSafeReference.h in Headers */, + CA0C6EED01AEF9503F563F22152B897A /* NSError+RLMSync.h in Headers */, + AFD326BE84ED28DC8621656AF856BDC1 /* Realm.h in Headers */, + 4C51D57FD3F184E87A83693D0FC367D8 /* RLMAccessor.h in Headers */, + 24847FB79D2CF8030E94D1EB8F084B0C /* RLMArray.h in Headers */, + 4B77541D5B5F0763633FC49F6460F6B5 /* RLMArray_Private.h in Headers */, + CD38830929E27B57CD57C6322A789B5A /* RLMCollection.h in Headers */, + 22F79E28426BDBB51323B498D5DD9D97 /* RLMCollection_Private.h in Headers */, + 320C2047153812DE7AB64AD57464857D /* RLMConstants.h in Headers */, + 696DFD8B1B417C7392CC52C46FCEA6D6 /* RLMListBase.h in Headers */, + E4D21C722D1855810159C19B14AA81DE /* RLMMigration.h in Headers */, + 12879CBF252177764BE31ADA5A5A3BD1 /* RLMMigration_Private.h in Headers */, + 8768811AE380F085C0D6693993409B18 /* RLMObject.h in Headers */, + F5B29BE3B9A66D09585475EDC6C68725 /* RLMObject_Private.h in Headers */, + 36E2B195CEEE05968F132C9189317832 /* RLMObjectBase.h in Headers */, + E504CADCE3A2F5EE67E1D57E36BFBFFE /* RLMObjectBase_Dynamic.h in Headers */, + F64E08F16FA9364EF87792B6283DA7FC /* RLMObjectBase_Private.h in Headers */, + 7FD640D5AB6F39FFF696D6F7E2C05959 /* RLMObjectSchema.h in Headers */, + 5CF10C1EDB9B4BA4C6909DA065E15F75 /* RLMObjectSchema_Private.h in Headers */, + F2361F1BB0755C6F76FD41C9EF958ADC /* RLMObjectStore.h in Headers */, + 8855C61A942C3B3B3B577420EF08AB79 /* RLMOptionalBase.h in Headers */, + 14815D0A1D15AE55C46865E21BEFB577 /* RLMPlatform.h in Headers */, + 2DDC0F1392ECF9DACDF75AED436B17F1 /* RLMProperty.h in Headers */, + A8BCC58476C92EB4E4AAD3344AA41317 /* RLMProperty_Private.h in Headers */, + 16C520E3A004519E6DBBF91DCA0BD24B /* RLMRealm+Sync.h in Headers */, + 63D110301D03DE07431C773697A7911F /* RLMRealm.h in Headers */, + AAF5D5D732267565F122BF98C1838B9A /* RLMRealm_Dynamic.h in Headers */, + 3BA9372F8F9A3885FBDC66DFB3536B59 /* RLMRealm_Private.h in Headers */, + 6AB1CD624CA00666B5D90F01CA9956D8 /* RLMRealmConfiguration+Sync.h in Headers */, + DC99FD7754461B54AB9DE02D686930D4 /* RLMRealmConfiguration.h in Headers */, + 9B3EDD6032DC60C5993FBA5C98E4B7A8 /* RLMRealmConfiguration_Private.h in Headers */, + 37085202D19F0204D0546AC692FC0A01 /* RLMResults.h in Headers */, + 816D2FDE9FC8575B1FA4515AA16233C4 /* RLMResults_Private.h in Headers */, + D9BDCC52C9C1CA379B17CC43D492AEBA /* RLMSchema.h in Headers */, + 9C0367D5799033228FE81CD8BB76947C /* RLMSchema_Private.h in Headers */, + CBDE2E90628F1C354B6E3CF645CFBAED /* RLMSyncConfiguration.h in Headers */, + 31739A239B975584242C0C5156B29F58 /* RLMSyncConfiguration_Private.h in Headers */, + 5115BF24865F21844361BD1899FB408B /* RLMSyncCredentials.h in Headers */, + 0E0A9D3F3990B117061FC5D459204484 /* RLMSyncManager.h in Headers */, + F66996C4CB8FBC49F236B864CB21B78C /* RLMSyncManager_Private.h in Headers */, + 9A41F5A03BD9A244943CC44B7AE5BD50 /* RLMSyncPermission.h in Headers */, + 15C90A014A579622A2C7C760611176AB /* RLMSyncSession.h in Headers */, + 781FA00782F7D7FC5FC7B37465D3DD0C /* RLMSyncSubscription.h in Headers */, + C806286B29BA9B21543D80642E4BA9C3 /* RLMSyncUser.h in Headers */, + DA8619EDE77C518675E4C03ECF8C7785 /* RLMSyncUtil.h in Headers */, + 31F19905E1A6E3E8D72A3776E8F6D340 /* RLMSyncUtil_Private.h in Headers */, + 17229DA3C3EFB4BF4A11DCAA21500DCF /* RLMThreadSafeReference.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1268,14 +1305,14 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ - 675A114988AE198732ED4AD726408964 /* Eureka */ = { + 0F32982340C6013DA81094C00BB2C7B5 /* Eureka */ = { isa = PBXNativeTarget; - buildConfigurationList = 50C8E1EFA0C5DC6AD96DBEE0EFA14308 /* Build configuration list for PBXNativeTarget "Eureka" */; + buildConfigurationList = BEEEC40F253F5FFDFB398B3A678C5BAB /* Build configuration list for PBXNativeTarget "Eureka" */; buildPhases = ( - B0652E9682BC86759CB160A39B336F2B /* Sources */, - 9A459D8E0AC5F79ADD60D9F6F4B8F1CB /* Frameworks */, - F0646B43B36FB653DE1592EFD881271F /* Resources */, - 1F412B9DD67AD97FE17C6F9513349CCA /* Headers */, + 53A1495AA6BC5D3602EF962A195BD75B /* Sources */, + BB669173F186BC19CAEC50D3F23D19F5 /* Frameworks */, + B6783F74A51F5B33CEDF78AE841ABB1A /* Resources */, + AE09BD9940CDDAECDD3E71594C156058 /* Headers */, ); buildRules = ( ); @@ -1364,11 +1401,11 @@ isa = PBXNativeTarget; buildConfigurationList = 0E68B89957875F63C749237ADEA3D249 /* Build configuration list for PBXNativeTarget "Realm" */; buildPhases = ( - 7C7C123EBEEA41D01C4385BEB112238C /* Sources */, - 277B5B91BF09E8DCBA6F28E123B04559 /* Frameworks */, - CA4163637E6167E837291501FBFB9A2B /* Headers */, - 28025138903B321FD11625053C9E0D56 /* Copy . Private Headers */, - 274115BD2ED7B62A7D2C2EC1A610F9F1 /* Copy . Public Headers */, + BF8F68EEEAC5C43AE3D57616CFE48451 /* Sources */, + 1ACE4902F7147CAD3CED9E1B2C0BB634 /* Frameworks */, + D4319C3F3DD8A6825FEFEAB7E81F5AF1 /* Headers */, + B1DFE0103C0A91B97C3BD945D05B4156 /* Copy . Private Headers */, + C5B3D2AE4BCE07316F3C7364B175E4CD /* Copy . Public Headers */, ); buildRules = ( ); @@ -1385,8 +1422,8 @@ D41D8CD98F00B204E9800998ECF8427E /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0700; + LastSwiftUpdateCheck = 0930; + LastUpgradeCheck = 0930; }; buildConfigurationList = 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */; compatibilityVersion = "Xcode 3.2"; @@ -1401,7 +1438,7 @@ projectRoot = ""; targets = ( 88E9EC28B8B46C3631E6B242B50F4442 /* Alamofire */, - 675A114988AE198732ED4AD726408964 /* Eureka */, + 0F32982340C6013DA81094C00BB2C7B5 /* Eureka */, 8CCBEDF06186AEA9DF4FE66479FF2589 /* Pods-Anyway */, F078210BAADA879878F87E9055309F2C /* Realm */, DC0FB29E0DE2BACABEAC3994D37732EC /* RealmSwift */, @@ -1411,11 +1448,11 @@ /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - F0646B43B36FB653DE1592EFD881271F /* Resources */ = { + B6783F74A51F5B33CEDF78AE841ABB1A /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - BD6FEBF93A8FB23057B38BC52342AF19 /* Eureka.bundle in Resources */, + 3821AF330FE77D79F58A716829D92996 /* Eureka.bundle in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1447,81 +1484,77 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 7C7C123EBEEA41D01C4385BEB112238C /* Sources */ = { + 53A1495AA6BC5D3602EF962A195BD75B /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 6A994F95AC9DF62B10F0DDFD560F7BDF /* collection_change_builder.cpp in Sources */, - 800858C78D1B635841E57207CED72E25 /* collection_notifications.cpp in Sources */, - 64983B09F0760BA0A1966B256A8F883B /* collection_notifier.cpp in Sources */, - 47284369D85B3F5D4E5AABC09C80F048 /* external_commit_helper.cpp in Sources */, - EDC407467AD35E26462E2E595C7A9AC0 /* format.cpp in Sources */, - 6EC9027D918EF8ADE1ED87EC99FD2250 /* index_set.cpp in Sources */, - E29DB2B065666CA02FD4BC57968C4A71 /* keychain_helper.cpp in Sources */, - 351DB8BE8B6CA23BC78D5D54C8F5FB9D /* list.cpp in Sources */, - E31295691D8A8114E312311EF403BDFD /* list_notifier.cpp in Sources */, - 1FFC323AD9671C953B21425F876E6A3F /* NSError+RLMSync.m in Sources */, - F74F2CAF27CBB42118FED9DEE3604EBB /* object.cpp in Sources */, - F721D2C02D5FE979E7638DC5FFB5FAA8 /* object_notifier.cpp in Sources */, - 049C5680FBE95156A76473F565839866 /* object_schema.cpp in Sources */, - 995EC88763825C70CC694BCA7EEDFE1A /* object_store.cpp in Sources */, - 8AD89A0DA3173352A5D18C5DF3AA333C /* placeholder.cpp in Sources */, - AE632CEA88677A7B6D1C5F5932986266 /* Realm-dummy.m in Sources */, - BB8B734691BFC0B1E2CF10CD4CA5857E /* realm_coordinator.cpp in Sources */, - B3D3B1C729C78880483502AC7C662E0E /* results.cpp in Sources */, - 1DC99B171D97F8B7916652C011712318 /* results_notifier.cpp in Sources */, - 09C5C995F970A0405FCA6CB8EFAB30C5 /* RLMAccessor.mm in Sources */, - E3B9AB2FF84700F819C0915DC920147C /* RLMAnalytics.mm in Sources */, - F360A8B4754FDA4EFE775E6D4547962D /* RLMArray.mm in Sources */, - 2CB0F8860FDA3855A545607B7DB337C5 /* RLMArrayLinkView.mm in Sources */, - 465C53B75AD9146F11AD2BEC3698D268 /* RLMAuthResponseModel.m in Sources */, - 3E2245F0E306A13226610E3841286673 /* RLMClassInfo.mm in Sources */, - C7C9DEA03660DCD66A101DCADB308C96 /* RLMCollection.mm in Sources */, - 44C8BD81D49B9DAE791A880871661896 /* RLMConstants.m in Sources */, - 54672FEBD3D00F547E71DA35A1ADE583 /* RLMListBase.mm in Sources */, - 90FDEA1F7BED538FC24102EF109987D6 /* RLMMigration.mm in Sources */, - ADCCBB44A2FDC9610AF79DD03F9812BB /* RLMNetworkClient.m in Sources */, - CB1802C7C49FC703CA11AA3F807E2A49 /* RLMObject.mm in Sources */, - 4D7C48608E43F3B056194EB8AA6213D5 /* RLMObjectBase.mm in Sources */, - 29623846A0DB1602F62E22155449F52A /* RLMObjectSchema.mm in Sources */, - 7A4C3B505A1F9899A1E61A8FC23F42C7 /* RLMObjectStore.mm in Sources */, - 362B59915C529779F5380F3F52C7A98E /* RLMObservation.mm in Sources */, - F2B28126157940CAADB85D64B32795AE /* RLMOptionalBase.mm in Sources */, - BA94E6BD0A3489E15B5ACE43E8FE67C7 /* RLMPredicateUtil.mm in Sources */, - 3720C7DF06CAC82BED96A2D65A3F2FC8 /* RLMProperty.mm in Sources */, - 1A98452905C05D17890F5A565091D38D /* RLMQueryUtil.mm in Sources */, - C7393325061C6DF2959B36FD0D581FF3 /* RLMRealm.mm in Sources */, - 94C170F089E28582865BBE1C36319F3A /* RLMRealmConfiguration+Sync.mm in Sources */, - 1A8BC03AFD56C7EA1656DC1D828FD2C7 /* RLMRealmConfiguration.mm in Sources */, - CEFF4977CABA1FF9E1629DBD857EC644 /* RLMRealmUtil.mm in Sources */, - 9A1C100D9F5A49B864F6CC42C5894900 /* RLMResults.mm in Sources */, - FBE464DA0DD491988B47B8B9E3FBF4BD /* RLMSchema.mm in Sources */, - 0C6AC71BA3BD053BE367C59005B75436 /* RLMSwiftSupport.m in Sources */, - B072733E59B22561B4C1038185F0D688 /* RLMSyncConfiguration.mm in Sources */, - C871C012ECCD48DB4C926F8A890C2474 /* RLMSyncCredentials.m in Sources */, - 12AADCB1FDC69E010AB5EE2322EB0ED5 /* RLMSyncErrorResponseModel.m in Sources */, - F235CE79B80EA08ADD4B422B5E07E287 /* RLMSyncManager.mm in Sources */, - 2DE065362AD086A43988AB0703DB9F48 /* RLMSyncPermissionChange.m in Sources */, - 9987E972D1CEE4FD2F35D70310341EE9 /* RLMSyncPermissionOffer.m in Sources */, - 1D996DF963ACA6E9879D2B57CCBF9490 /* RLMSyncPermissionOfferResponse.m in Sources */, - BD5F09163374DDDEC7F990F6E32C57C8 /* RLMSyncSession.mm in Sources */, - 2E72509E4A89E42ADA5925A9AA36730A /* RLMSyncSessionRefreshHandle.mm in Sources */, - 81F432D75037041DE0DC1CE348DA8B1D /* RLMSyncUser.mm in Sources */, - 42749527759AEEA707A47239DE68B1B5 /* RLMSyncUtil.mm in Sources */, - BB1BD27373ECC97C862538EEA7E18643 /* RLMThreadSafeReference.mm in Sources */, - DBA264E204F15C44AC678A611A2D60EC /* RLMTokenModels.m in Sources */, - 0AD4C6175BDD69CE078B71611E208EE5 /* RLMUpdateChecker.mm in Sources */, - 6E032CEF899312F1D255ED1E5BA0EDF4 /* RLMUtil.mm in Sources */, - 4F40E7A85AA630F4FD3A6B091319E3ED /* schema.cpp in Sources */, - A429504524879F6BA5F4A3DB8B5DAB09 /* shared_realm.cpp in Sources */, - 101FAFF668DB8CBB0AD05263D1314761 /* sync_file.cpp in Sources */, - 52EF95446C4BAB921DC82EB006C739C0 /* sync_manager.cpp in Sources */, - E1DD9D55EE4D1BFFB4F09DAE9EED98E5 /* sync_metadata.cpp in Sources */, - 5B43EE354D8587C3925BC6D805772DBF /* sync_session.cpp in Sources */, - 2BC8857FCF261B782B8198875DD8C7E9 /* sync_user.cpp in Sources */, - F7D14B1756A81FB8BDD58A4B2BEF110A /* thread_safe_reference.cpp in Sources */, - 3D0274E408AD3EA2AFC308A983831163 /* transact_log_handler.cpp in Sources */, - 15B32D07613721F00FBBDDACBC04B140 /* weak_realm_notifier.cpp in Sources */, + B4E2C479A4F5AE6B623358BA627CBF7E /* ActionSheetRow.swift in Sources */, + DF1FF7E623ECEC3F3AAD33544C48EBE2 /* AlertOptionsRow.swift in Sources */, + 2E97216C1C7F9F2CD9BC52EA234127CC /* AlertRow.swift in Sources */, + B39E3EC7DD02DBBEB8191E4907B182F2 /* BaseRow.swift in Sources */, + 287BE75854BFBB3B49A3CE3268F4E022 /* ButtonRow.swift in Sources */, + 012D2917EAF8A5066C7D301EF17159B6 /* ButtonRowWithPresent.swift in Sources */, + 84C1EEEFE6BAEB404BDF1DFE80B0103A /* Cell.swift in Sources */, + 8C9745983CF09DBCE1962EBA38AABC92 /* CellType.swift in Sources */, + 87B6F6EFDC0598100FCD185D779845C3 /* CheckRow.swift in Sources */, + 9F26EAAD38045F100581AE859D510859 /* Core.swift in Sources */, + 7C44A2AD32395ECFCAD94ABBD590130E /* DateFieldRow.swift in Sources */, + 5A3516009F452DE4C52D34D9E1F55F6E /* DateInlineFieldRow.swift in Sources */, + 0BBEC18C19AD617FB56516D3CDEC6082 /* DateInlineRow.swift in Sources */, + 9F9C4CF9CF78B1D3999F73F71D5EFA23 /* DatePickerRow.swift in Sources */, + 914DEA5309A0973CC4B0E4FE333CFD3A /* DateRow.swift in Sources */, + 5533F297F16DE0A8BB487D2A1CA94395 /* DecimalFormatter.swift in Sources */, + C8682BB725C720E6B2A85D5B8778DCF3 /* DoublePickerInputRow.swift in Sources */, + B56DA7CCA71E56DDA822E43471168DD5 /* DoublePickerRow.swift in Sources */, + 19AFC8077796C94D7DD31B0E7F50DB03 /* Eureka-dummy.m in Sources */, + 44627784F3ECCC248BAF8B01003A2FEA /* FieldRow.swift in Sources */, + A91E02EB3D436D81C1F7F4BDAD695DE4 /* FieldsRow.swift in Sources */, + A42730CE505B4B152DC3BD016CECB938 /* Form.swift in Sources */, + 4F8B3D0568E65D9C804FB9621F68E192 /* GenericMultipleSelectorRow.swift in Sources */, + 26565FD8C9960FF8904D2CAAB2C6BB4B /* HeaderFooterView.swift in Sources */, + 8C5A01880B66C01C84223F4EF4C474BE /* Helpers.swift in Sources */, + B758518039F59EE5954420C880AD99A5 /* InlineRowType.swift in Sources */, + 0572E357DE1A281CDA359D2C9A0FCD58 /* LabelRow.swift in Sources */, + 92557580872DDD0A6891419D1DB8BE2B /* ListCheckRow.swift in Sources */, + 9844C9BB8B73E745AAC35278010B7762 /* MultipleSelectorRow.swift in Sources */, + B74A338ED05D70855ED500375705FA25 /* MultipleSelectorViewController.swift in Sources */, + 5CEC5970C4A3C1FEA706B041EE93925E /* NavigationAccessoryView.swift in Sources */, + 74148E1FE3BB750800D605008ADAD720 /* Operators.swift in Sources */, + 30E7256B41BA0DABAA10CC68D8D7BF53 /* OptionsRow.swift in Sources */, + B3D4C29C195EC854278DE1C3604C85B8 /* PickerInlineRow.swift in Sources */, + 5C1834BDC03355D57DB77D07B07A117D /* PickerInputRow.swift in Sources */, + 48ED1E1E86AA99B1B89422323D039D47 /* PickerRow.swift in Sources */, + 8E930C3BFE5E90BB07909F4AA2F8DDFE /* PopoverSelectorRow.swift in Sources */, + F3EC6A7ED8D205C0D2FE2D4462D9A5D4 /* PresenterRowType.swift in Sources */, + 4CF684A09D3BDDA366B0856F77227215 /* Protocols.swift in Sources */, + BC4AD0347C5C33B53016C97AFEE1AF63 /* PushRow.swift in Sources */, + 1C93D86DA0AEB40028BABC3A3DE73B7F /* Row.swift in Sources */, + 25D8D0458BCA197BAC764DFC2CBFD289 /* RowControllerType.swift in Sources */, + 4EA55BBBB181C6E3A739759236173A18 /* RowProtocols.swift in Sources */, + E9EB37F7808C9484BA6A07AA81CA0387 /* RowType.swift in Sources */, + 0E489B667253F643A20163185C0684E3 /* RuleClosure.swift in Sources */, + 7EF3713799FE9AC463D4BE55EC128231 /* RuleEmail.swift in Sources */, + FA9F82B0CF2BD9866010CD559772A554 /* RuleEqualsToRow.swift in Sources */, + FC5F8EA3DC8F60DB05E902A96EB4A14B /* RuleLength.swift in Sources */, + 2634B2AB72D5D8ED489591213E014936 /* RuleRange.swift in Sources */, + D400533601A5D7376A13F567908850C3 /* RuleRegExp.swift in Sources */, + 4D3F908669ABA61E3D4054A297F60755 /* RuleRequired.swift in Sources */, + D6E282CFDA0176397A3E0B6F28A7FEA3 /* RuleURL.swift in Sources */, + BCF869F848BDFB858BF8839651612BA4 /* Section.swift in Sources */, + 7ED7B21E88EDE7A0293EC6EDEC6A9DD5 /* SegmentedRow.swift in Sources */, + AE34EC79435E2F68A678985572D16FEE /* SelectableRowType.swift in Sources */, + F0B9289FF41C540C4800B15E74538E5B /* SelectableSection.swift in Sources */, + 03F67C413386BDBE1556F0A672EE9DFA /* SelectorAlertController.swift in Sources */, + 45A30147FA0F0703067D5175F610A00F /* SelectorRow.swift in Sources */, + 3E645A4D6950FF94D944EF8DB15E294E /* SelectorViewController.swift in Sources */, + 3DAA106B3684A06FEA830E090A697132 /* SliderRow.swift in Sources */, + EFA9D9000808D5BB24FE8CF33E085A0E /* StepperRow.swift in Sources */, + 279A83DC9F2C01F480BC112A20B6873C /* SwipeActions.swift in Sources */, + 877A0A159511077573CF194DF28D8037 /* SwitchRow.swift in Sources */, + 982E9EF335092CDDC4FC32BDD5DB15EF /* TextAreaRow.swift in Sources */, + 0D50E31A4C7A910EBBC0DADB0FA6971A /* TriplePickerInputRow.swift in Sources */, + A7B38585E110183B601BE3FDCE7CF24F /* TriplePickerRow.swift in Sources */, + 03F7C84AC2C8A9ED1DB5142A397702CF /* Validation.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1542,71 +1575,88 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - B0652E9682BC86759CB160A39B336F2B /* Sources */ = { + BF8F68EEEAC5C43AE3D57616CFE48451 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 2A179EA67ECDDBB23F30CF0B0A71A617 /* ActionSheetRow.swift in Sources */, - 654744F1D8E5A8DB4711E81DC6CC99C3 /* AlertRow.swift in Sources */, - 7D4103D396B2519600255CA676DE1D62 /* BaseRow.swift in Sources */, - AA0CDD726F8156D06FDFE3C6FAB27C52 /* ButtonRow.swift in Sources */, - 1ACA00A9EC125081B88B9228462431B8 /* ButtonRowWithPresent.swift in Sources */, - 9926EF73DC7EFCBD50D322671328B24F /* Cell.swift in Sources */, - 144A795542A55FCA27F839FD8B3848AD /* CellType.swift in Sources */, - 1D4CD70DC32B037FCD6320FE079695D5 /* CheckRow.swift in Sources */, - 02F07D843F3B3151880D1E67205C11A0 /* Core.swift in Sources */, - 265A3BCF8B426717E8EE1CAD4341F99A /* DateFieldRow.swift in Sources */, - 7CE512D69EDD58D7DDD7B66BE6592EED /* DateInlineFieldRow.swift in Sources */, - 2189F5569BACCE66FD41F5E58FF351B8 /* DateInlineRow.swift in Sources */, - 5D647533060C688C633E15C86553B868 /* DatePickerRow.swift in Sources */, - 6F9F25DE63532C8DE5077822E6316111 /* DateRow.swift in Sources */, - E585A7898FC181529FD2704BEF4CE992 /* DecimalFormatter.swift in Sources */, - 5A380E0127635057397F45AFBD13B255 /* Eureka-dummy.m in Sources */, - 23E6887F5ADC8D4917BC12B079864E1D /* FieldRow.swift in Sources */, - F2095372A5BB341F1B5469F634EA6687 /* FieldsRow.swift in Sources */, - 0C19B0ADBDDF9D1F35443473DD32F58C /* Form.swift in Sources */, - A38A545D1ADAFAB39F6A28B499D9AAB9 /* GenericMultipleSelectorRow.swift in Sources */, - F1681F8D16AD91A13187576F524783A8 /* HeaderFooterView.swift in Sources */, - A0E6C2F2A364C6349BD2B79F520A8958 /* Helpers.swift in Sources */, - FF0168C08BB3555924897006B4AD36DA /* InlineRowType.swift in Sources */, - 57F029A6EAD66812E9B0C1319AFE527B /* LabelRow.swift in Sources */, - 28500C67AF0CB1EB34B53FFA93C3D27F /* ListCheckRow.swift in Sources */, - 76110C1B9D1144D99D2C179DC74121DD /* MultipleSelectorRow.swift in Sources */, - 59947C008333007F823B0922EE87F96B /* MultipleSelectorViewController.swift in Sources */, - 1EEFBE2820F6BEAC9BAD184673293C9E /* NavigationAccessoryView.swift in Sources */, - 7B19019174C42D7D481C1B120272443B /* Operators.swift in Sources */, - CCAB89C5B0F52B0F69DBED084DDFCFBB /* OptionsRow.swift in Sources */, - 0B2FCAF300C1B74DA6343E48B7D5F3F6 /* PickerInlineRow.swift in Sources */, - 9CD46DF12CFA5C561B75EF20B95EECD0 /* PickerInputRow.swift in Sources */, - 1C5B941F79B39828B27EAAD3C9AF7B4B /* PickerRow.swift in Sources */, - 17048D78835182A0B4E9D7926DA75DAD /* PopoverSelectorRow.swift in Sources */, - 4EA524A2BD2D970745B89DAADDF7719E /* PresenterRowType.swift in Sources */, - 0F47F6ABDFD61B71965C06B833435E53 /* Protocols.swift in Sources */, - 29DBA0C923DE13772022DB52DBAF3CEE /* PushRow.swift in Sources */, - 1772EAFC71C30BD931A9585212CA03AC /* Row.swift in Sources */, - 17A3FB1B471D1C1A61BCFBBF5831D845 /* RowControllerType.swift in Sources */, - DE6DF70A452BF20CB858BA7072AAE3EA /* RowProtocols.swift in Sources */, - BFE53EDC99E6B10F19E85ECBD01FB33E /* RowType.swift in Sources */, - E490A481C69CE189A0814626B1251FB4 /* RuleClosure.swift in Sources */, - 987A9AA62B48BC871C21106E68F9257E /* RuleEmail.swift in Sources */, - AA7012D4A2E0818B5D6C2BAA5DFA3CD2 /* RuleEqualsToRow.swift in Sources */, - C657B4C0DFB6C3BE1119C1BB8CAD4C78 /* RuleLength.swift in Sources */, - E0946CE4B110F2A64041FAB63546FB48 /* RuleRange.swift in Sources */, - E92B5113A74D07D38C8137460AE0E3C7 /* RuleRegExp.swift in Sources */, - BB04DAD5C6DCAE93C15FED1090520610 /* RuleRequired.swift in Sources */, - 827BD4C80F28C2F7E9521461DA5CBDAE /* RuleURL.swift in Sources */, - 51F62349D05FCB3B6E1E8AD406DEEFE7 /* Section.swift in Sources */, - 04A9A536DFAF3C5EC723F92EED127215 /* SegmentedRow.swift in Sources */, - FC505268B5A20F0F00D3BC0A9FF66F99 /* SelectableRowType.swift in Sources */, - D9284875BF00A2033E41A8252CE907A5 /* SelectableSection.swift in Sources */, - B3B5B10201D9BA6470173163A6F6725E /* SelectorAlertController.swift in Sources */, - DB5214501DA0045F69B3F336F0AB11E8 /* SelectorRow.swift in Sources */, - 25A70264F9301E7603D9883777DE8050 /* SelectorViewController.swift in Sources */, - 306BD477167820D6CDECDCE1D7445ECD /* SliderRow.swift in Sources */, - 99BC63A28EB55DCDC2CA8EAC1886E077 /* StepperRow.swift in Sources */, - 3E89604392C5EBDC5E27937AA57C5AC0 /* SwitchRow.swift in Sources */, - B5BA32808AFF5DC9C2723CD85931B9DB /* TextAreaRow.swift in Sources */, - A5B27A12053041F38D47538E48678881 /* Validation.swift in Sources */, + C2DC4AD2F3995AA81707794689F8CF74 /* binding_callback_thread_observer.cpp in Sources */, + 5189820E69253506FD7310042AD7926F /* collection_change_builder.cpp in Sources */, + 0B8EB6B80F7C87AEE216BF9BB474CC0C /* collection_notifications.cpp in Sources */, + 6CF8302A330790C27A1C7B32EB57B2AF /* collection_notifier.cpp in Sources */, + D34F77832B9FF78147510224EB7F6B64 /* external_commit_helper.cpp in Sources */, + F1DE0BB5E1E6C7CD3A266C5E2B6648CF /* index_set.cpp in Sources */, + B57FFE0778AE7613074B8D785EE3D125 /* keychain_helper.cpp in Sources */, + A9970438CE8FC3C3832BAEB2B0B8338E /* list.cpp in Sources */, + 6669F28B1A2C7D31D1220BFE607F5FE8 /* list_notifier.cpp in Sources */, + 40AB45ED67B70B7533B640451D1471BE /* network_reachability_observer.cpp in Sources */, + 865B7CA0F73C124C27871A17AC8B125A /* NSError+RLMSync.m in Sources */, + C02E824B5FA3AE349B3BF707777CE598 /* object.cpp in Sources */, + 4F7EA69A18363E1D36DAB81FA099937B /* object_notifier.cpp in Sources */, + 48B8426CFA7B8518DC2FD4E82EAFE8BF /* object_schema.cpp in Sources */, + 1FF47336119005CEC78DA4DA843F2FB8 /* object_store.cpp in Sources */, + DA72EE1BEECBFC3AD445F07F8841B779 /* partial_sync.cpp in Sources */, + 71E24C6488405AB4259428CB5DBBBBDD /* placeholder.cpp in Sources */, + 3C84291DDD10DE22CC5DB09B6C970EA1 /* primitive_list_notifier.cpp in Sources */, + 568184DF93AC3C9DFB2F24557395B7DE /* Realm-dummy.m in Sources */, + B54D247B1E7212BA3402333CC499B1AC /* realm_coordinator.cpp in Sources */, + 0B101BA9AF54D6628C9988EB0C11DDD4 /* results.cpp in Sources */, + 3E0574746ACD3430FBC2E5CBF9B0E0EF /* results_notifier.cpp in Sources */, + 4AECEECAFC2366D7C30D8BD6F84FD953 /* RLMAccessor.mm in Sources */, + 1CA80508B65F46016A58E5A8B69366BC /* RLMAnalytics.mm in Sources */, + AE68B96A894BB61D13DB1DBE19DFED69 /* RLMArray.mm in Sources */, + 848D93A72FA1AC760E63B413CBAA6DB7 /* RLMClassInfo.mm in Sources */, + 62D97A11B64C00B0EB740B019D5EE504 /* RLMCollection.mm in Sources */, + F6F450D42B629A0DB2656A45C27104E5 /* RLMConstants.m in Sources */, + 17E224A6EC632ECE28E85F5634F6BA7F /* RLMJSONModels.m in Sources */, + 8D4D3BAF2CA1B006A5A166278D0493D6 /* RLMListBase.mm in Sources */, + D0155F699048D635CFDE7B6400B2276D /* RLMManagedArray.mm in Sources */, + 9C6FD8BBBFAC7CB6AC73E486753F0A61 /* RLMMigration.mm in Sources */, + BC159338C06BCE13FCB2218A76131CF1 /* RLMNetworkClient.mm in Sources */, + DA6591EDA5CC079BD109D60FBC09C86F /* RLMObject.mm in Sources */, + 9ED1CB54A6C6476AAE61ACB8C2775527 /* RLMObjectBase.mm in Sources */, + 89EBE6099502CFF9662F446DCC6A4482 /* RLMObjectSchema.mm in Sources */, + 4CB3985C8BE626353E4351A9DBA4AA62 /* RLMObjectStore.mm in Sources */, + FA036C2A3039E362C114476EFEE4A2AF /* RLMObservation.mm in Sources */, + 05FA77B0DE799837578ABC6D82C06549 /* RLMOptionalBase.mm in Sources */, + 5AEF2A6ADBB063F04EA765993A579912 /* RLMPredicateUtil.mm in Sources */, + 3FAD614E68947DA3994519A37F8056B9 /* RLMProperty.mm in Sources */, + DF1E2EE1590F5AF0A383E45F0EB8592F /* RLMQueryUtil.mm in Sources */, + 2035148E28F106A8F2E40C5E49DEDAFC /* RLMRealm+Sync.mm in Sources */, + 79A0F3AC5DC49A981CCC5FE6EAE148C2 /* RLMRealm.mm in Sources */, + 0A2BA5C92C965AD75DE3421529F36FEE /* RLMRealmConfiguration+Sync.mm in Sources */, + C5BBAB3319B59F0C6A4797FEC3F2CB2D /* RLMRealmConfiguration.mm in Sources */, + F7B5A8CD4BC71DD2FEBCC0E4FF3CB6EC /* RLMRealmUtil.mm in Sources */, + 478664BBB23B1EAD57A4A5C76E673252 /* RLMResults.mm in Sources */, + 7630E6F54BD665C6CD942A4983349B4A /* RLMSchema.mm in Sources */, + 34F518D2BA46F7163960780654DD00A4 /* RLMSwiftSupport.m in Sources */, + ECAC9E27655B03ED369DEE7A147F841D /* RLMSyncConfiguration.mm in Sources */, + 18DFEBB2A5BCF28AF31909F0D1BE38F3 /* RLMSyncCredentials.m in Sources */, + 778953B600956A438009F452199B67EA /* RLMSyncManager.mm in Sources */, + 7FB875528634DFE291B4B6CC882655E9 /* RLMSyncPermission.mm in Sources */, + 6B8B6D0E5918C6FB627EE302EC3E648E /* RLMSyncPermissionResults.mm in Sources */, + AA910A5A283B3593AEA0185CED8E86ED /* RLMSyncSession.mm in Sources */, + DD7D0FB52D78BFA107C8F63F80F93A2D /* RLMSyncSessionRefreshHandle.mm in Sources */, + 0C842CF4F10ACA094DC9A3AA79CE9D79 /* RLMSyncSubscription.mm in Sources */, + C3D4AF46447ABEC455E52787E49067B0 /* RLMSyncUser.mm in Sources */, + 8F194A5CD188F7E0E82B1C12621450E3 /* RLMSyncUtil.mm in Sources */, + 2DE681521553036E7A9313A34996453A /* RLMThreadSafeReference.mm in Sources */, + ADA7C9C45375006ACA49439ADB1BA9E7 /* RLMUpdateChecker.mm in Sources */, + 46B6797F757C3C01F8B7283C22D50E16 /* RLMUtil.mm in Sources */, + 8C7FD8DFA011246E17D3C7E85E346314 /* schema.cpp in Sources */, + 46E811FE94B21D3D715CE3B737C8E8E1 /* shared_realm.cpp in Sources */, + 59AFC3E058DFA888D3336C284D9ECAC5 /* sync_config.cpp in Sources */, + A9F1AF3B8BEFC83ABD3B631AA34A321B /* sync_file.cpp in Sources */, + 113E540A48C9788488C829D064F0426C /* sync_manager.cpp in Sources */, + 50FAECEF7E678D9282B5D9AAB29953D9 /* sync_metadata.cpp in Sources */, + FC53556EE2B2DED63AB57C1D739250FC /* sync_permission.cpp in Sources */, + B958E5C3EE5DE7263608B4AFE58C12C7 /* sync_session.cpp in Sources */, + 31EA6AF4ACD9318764BD6B4FC777D28F /* sync_user.cpp in Sources */, + 6C3CCBDE76FDB1C55A8AE0980BEA0CF8 /* system_configuration.cpp in Sources */, + 3782E7EBB5E73BA7BE1EC8890FA347AA /* thread_safe_reference.cpp in Sources */, + FCEEBA45523D3EDA2F681A06F92038DB /* transact_log_handler.cpp in Sources */, + 6C1EB5D1744D0E29EA224745A24F0C6F /* uuid.cpp in Sources */, + 3EA100AAD29DAB00C8DCB029A5512C1C /* weak_realm_notifier.cpp in Sources */, + D4E45F75BD63B29682B6C23CF9C3C662 /* work_queue.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1674,490 +1724,525 @@ E4162DDA4CFF176B5242D7223D26DE62 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = Eureka; - target = 675A114988AE198732ED4AD726408964 /* Eureka */; + target = 0F32982340C6013DA81094C00BB2C7B5 /* Eureka */; targetProxy = 6254B25DC51CD130DA922C3A00E1AB65 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ - 015A368F878AC3E2CEAE21DDE8026304 /* Debug */ = { + 04BB07A9A550A0156EBBF4CFA8A3E091 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 370D52E54F48A531CF8544EAD9E80C15 /* Realm.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/Realm/Realm-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Realm/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/Realm/Realm.modulemap"; + PRODUCT_MODULE_NAME = Realm; + PRODUCT_NAME = Realm; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 1EE19F5DD95931924296F637BF18BD8F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGNING_ALLOWED = NO; CODE_SIGNING_REQUIRED = NO; COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "POD_CONFIGURATION_DEBUG=1", "DEBUG=1", "$(inherited)", ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; - PROVISIONING_PROFILE_SPECIFIER = NO_SIGNING/; + PRODUCT_NAME = "$(TARGET_NAME)"; STRIP_INSTALLED_PRODUCT = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SYMROOT = "${SRCROOT}/../build"; }; name = Debug; }; - 0A29B6F510198AF64EFD762EF6FA97A5 /* Release */ = { + 29A7CBD6C628E1F2F5D9B17B681B11A3 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = FFF83427A564921CED22CDEB18ECCD69 /* Alamofire.xcconfig */; + baseConfigurationReference = 370D52E54F48A531CF8544EAD9E80C15 /* Realm.xcconfig */; buildSettings = { + CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_NO_COMMON_BLOCKS = YES; - GCC_PREFIX_HEADER = "Target Support Files/Alamofire/Alamofire-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/Alamofire/Info.plist"; + GCC_PREFIX_HEADER = "Target Support Files/Realm/Realm-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Realm/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MODULEMAP_FILE = "Target Support Files/Alamofire/Alamofire.modulemap"; - MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_NAME = Alamofire; + MODULEMAP_FILE = "Target Support Files/Realm/Realm.modulemap"; + PRODUCT_MODULE_NAME = Realm; + PRODUCT_NAME = Realm; SDKROOT = iphoneos; SKIP_INSTALL = YES; - SWIFT_VERSION = 3.0; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = Release; + name = Debug; }; - 0AA6BC82AD7877E120FF86C425DF245B /* Release */ = { + 349F0ABA411C1CC4D2026225F5759B35 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 2B46C6700BBE28BFE8DE7287BD5F560B /* Realm.xcconfig */; + baseConfigurationReference = 4DD427720AB8D7C10B9C2F9F6B691617 /* Eureka.xcconfig */; buildSettings = { + CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_NO_COMMON_BLOCKS = YES; - GCC_PREFIX_HEADER = "Target Support Files/Realm/Realm-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/Realm/Info.plist"; + GCC_PREFIX_HEADER = "Target Support Files/Eureka/Eureka-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Eureka/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MODULEMAP_FILE = "Target Support Files/Realm/Realm.modulemap"; - MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_NAME = Realm; + MODULEMAP_FILE = "Target Support Files/Eureka/Eureka.modulemap"; + PRODUCT_MODULE_NAME = Eureka; + PRODUCT_NAME = Eureka; SDKROOT = iphoneos; SKIP_INSTALL = YES; - SWIFT_VERSION = 3.0; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = Release; + name = Debug; }; - 0EA4EB270ABBD0D8945C050B09AD6F73 /* Debug */ = { + 49D7EC8F5AACF86F11626E64C9D1BEC3 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 5A974C2BACAA1BC8BD9AB9085A77C421 /* Pods-Anyway.debug.xcconfig */; + baseConfigurationReference = EB9446C2B4219C267A6A53A4A1FA6017 /* RealmSwift.xcconfig */; buildSettings = { + CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_NO_COMMON_BLOCKS = YES; - INFOPLIST_FILE = "Target Support Files/Pods-Anyway/Info.plist"; + GCC_PREFIX_HEADER = "Target Support Files/RealmSwift/RealmSwift-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/RealmSwift/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-Anyway/Pods-Anyway.modulemap"; - MTL_ENABLE_DEBUG_INFO = YES; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = Pods_Anyway; + MODULEMAP_FILE = "Target Support Files/RealmSwift/RealmSwift.modulemap"; + PRODUCT_MODULE_NAME = RealmSwift; + PRODUCT_NAME = RealmSwift; SDKROOT = iphoneos; SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = Debug; + name = Release; }; - 26223D1FCCF4EA26300D8AC8D8D0D6CC /* Release */ = { + 4ED57640784516C1F626212129750E2B /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 8436E1EDA6FAD0175D5D6728FA4DF0CA /* SwiftyJSON.xcconfig */; + baseConfigurationReference = EB9446C2B4219C267A6A53A4A1FA6017 /* RealmSwift.xcconfig */; buildSettings = { + CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_NO_COMMON_BLOCKS = YES; - GCC_PREFIX_HEADER = "Target Support Files/SwiftyJSON/SwiftyJSON-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/SwiftyJSON/Info.plist"; + GCC_PREFIX_HEADER = "Target Support Files/RealmSwift/RealmSwift-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/RealmSwift/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MODULEMAP_FILE = "Target Support Files/SwiftyJSON/SwiftyJSON.modulemap"; - MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_NAME = SwiftyJSON; + MODULEMAP_FILE = "Target Support Files/RealmSwift/RealmSwift.modulemap"; + PRODUCT_MODULE_NAME = RealmSwift; + PRODUCT_NAME = RealmSwift; SDKROOT = iphoneos; SKIP_INSTALL = YES; - SWIFT_VERSION = 3.0; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = Release; + name = Debug; }; - 448C7F3969F6BC7730858C439F6B001F /* Release */ = { + 69C9940146907A07D5E6B8D2A6FC8749 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A3E19E8BDBA82BE8168AE37FA9C1BF21 /* Pods-Anyway.release.xcconfig */; + baseConfigurationReference = AA392BA26630759D689717E517366E41 /* SwiftyJSON.xcconfig */; buildSettings = { + CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_NO_COMMON_BLOCKS = YES; - INFOPLIST_FILE = "Target Support Files/Pods-Anyway/Info.plist"; + GCC_PREFIX_HEADER = "Target Support Files/SwiftyJSON/SwiftyJSON-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/SwiftyJSON/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-Anyway/Pods-Anyway.modulemap"; - MTL_ENABLE_DEBUG_INFO = NO; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = Pods_Anyway; + MODULEMAP_FILE = "Target Support Files/SwiftyJSON/SwiftyJSON.modulemap"; + PRODUCT_MODULE_NAME = SwiftyJSON; + PRODUCT_NAME = SwiftyJSON; SDKROOT = iphoneos; SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = Release; - }; - 44CDBB6D11DE06DB64D6268622BDC47E /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGNING_REQUIRED = NO; - COPY_PHASE_STRIP = YES; - ENABLE_NS_ASSERTIONS = NO; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_PREPROCESSOR_DEFINITIONS = ( - "POD_CONFIGURATION_RELEASE=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - PROVISIONING_PROFILE_SPECIFIER = NO_SIGNING/; - STRIP_INSTALLED_PRODUCT = NO; - SYMROOT = "${SRCROOT}/../build"; - VALIDATE_PRODUCT = YES; - }; - name = Release; + name = Debug; }; - 5B736E2F72E724B0A86BF9B011C2B6A3 /* Release */ = { + A18C9CC7E37563F2891E7DC87F2DE6CE /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 86382517D56D223CE543C3F1067B3358 /* Eureka.xcconfig */; + baseConfigurationReference = 43B756E9365921724D5897717226AE33 /* Alamofire.xcconfig */; buildSettings = { + CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_NO_COMMON_BLOCKS = YES; - GCC_PREFIX_HEADER = "Target Support Files/Eureka/Eureka-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/Eureka/Info.plist"; + GCC_PREFIX_HEADER = "Target Support Files/Alamofire/Alamofire-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Alamofire/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MODULEMAP_FILE = "Target Support Files/Eureka/Eureka.modulemap"; - MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_NAME = Eureka; + MODULEMAP_FILE = "Target Support Files/Alamofire/Alamofire.modulemap"; + PRODUCT_MODULE_NAME = Alamofire; + PRODUCT_NAME = Alamofire; SDKROOT = iphoneos; SKIP_INSTALL = YES; - SWIFT_VERSION = 3.0; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; - 8569249F085AB6D7BCF921BDCE510CA0 /* Debug */ = { + AD619FDDE8FC7869F438098A8DD0E440 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 8436E1EDA6FAD0175D5D6728FA4DF0CA /* SwiftyJSON.xcconfig */; + baseConfigurationReference = AA392BA26630759D689717E517366E41 /* SwiftyJSON.xcconfig */; buildSettings = { + CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/SwiftyJSON/SwiftyJSON-prefix.pch"; INFOPLIST_FILE = "Target Support Files/SwiftyJSON/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/SwiftyJSON/SwiftyJSON.modulemap"; - MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_MODULE_NAME = SwiftyJSON; PRODUCT_NAME = SwiftyJSON; SDKROOT = iphoneos; SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = Debug; + name = Release; }; - 93B9DC730F67DD5F78206546BEE74B08 /* Debug */ = { + B084585D96EFD33BB3E515F7EDD9E699 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 2B46C6700BBE28BFE8DE7287BD5F560B /* Realm.xcconfig */; + baseConfigurationReference = A0DA0FEA1E28CCA7CC5072098C38DC6F /* Pods-Anyway.debug.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_NO_COMMON_BLOCKS = YES; - GCC_PREFIX_HEADER = "Target Support Files/Realm/Realm-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/Realm/Info.plist"; + INFOPLIST_FILE = "Target Support Files/Pods-Anyway/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MODULEMAP_FILE = "Target Support Files/Realm/Realm.modulemap"; - MTL_ENABLE_DEBUG_INFO = YES; - PRODUCT_NAME = Realm; + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-Anyway/Pods-Anyway.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = iphoneos; SKIP_INSTALL = YES; - SWIFT_VERSION = 3.0; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; - B4EFF44FB30498779D7B363BDACF73AC /* Debug */ = { + CE400855FA0980198C0B2AE3C94E89C2 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 86382517D56D223CE543C3F1067B3358 /* Eureka.xcconfig */; + baseConfigurationReference = 4DD427720AB8D7C10B9C2F9F6B691617 /* Eureka.xcconfig */; buildSettings = { + CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Target Support Files/Eureka/Eureka-prefix.pch"; INFOPLIST_FILE = "Target Support Files/Eureka/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/Eureka/Eureka.modulemap"; - MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_MODULE_NAME = Eureka; PRODUCT_NAME = Eureka; SDKROOT = iphoneos; SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = Debug; + name = Release; }; - BF96FC4E417B90B65C31D6E388601B00 /* Release */ = { + EA0ADC75B20BB62958101306980460E6 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F6A81900E2DEB7EFBC9DFAAD9B91F191 /* RealmSwift.xcconfig */; + baseConfigurationReference = 43B756E9365921724D5897717226AE33 /* Alamofire.xcconfig */; buildSettings = { + CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_NO_COMMON_BLOCKS = YES; - GCC_PREFIX_HEADER = "Target Support Files/RealmSwift/RealmSwift-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/RealmSwift/Info.plist"; + GCC_PREFIX_HEADER = "Target Support Files/Alamofire/Alamofire-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Alamofire/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MODULEMAP_FILE = "Target Support Files/RealmSwift/RealmSwift.modulemap"; - MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_NAME = RealmSwift; + MODULEMAP_FILE = "Target Support Files/Alamofire/Alamofire.modulemap"; + PRODUCT_MODULE_NAME = Alamofire; + PRODUCT_NAME = Alamofire; SDKROOT = iphoneos; SKIP_INSTALL = YES; - SWIFT_VERSION = 3.0; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = Release; + name = Debug; }; - C1E6E755215ED021B069461A42EE2734 /* Debug */ = { + F4568DEE257655D290C2B9CEAB37C934 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F6A81900E2DEB7EFBC9DFAAD9B91F191 /* RealmSwift.xcconfig */; buildSettings = { - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGNING_ALLOWED = NO; + CODE_SIGNING_REQUIRED = NO; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; - GCC_PREFIX_HEADER = "Target Support Files/RealmSwift/RealmSwift-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/RealmSwift/Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_RELEASE=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MODULEMAP_FILE = "Target Support Files/RealmSwift/RealmSwift.modulemap"; - MTL_ENABLE_DEBUG_INFO = YES; - PRODUCT_NAME = RealmSwift; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = NO; + SYMROOT = "${SRCROOT}/../build"; }; - name = Debug; + name = Release; }; - F383079BFBF927813EA3613CFB679FDE /* Debug */ = { + F5183957ECD100E68CDD3924E4D1125F /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = FFF83427A564921CED22CDEB18ECCD69 /* Alamofire.xcconfig */; + baseConfigurationReference = FF5A889E69302012309706AA00CEA87E /* Pods-Anyway.release.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_NO_COMMON_BLOCKS = YES; - GCC_PREFIX_HEADER = "Target Support Files/Alamofire/Alamofire-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/Alamofire/Info.plist"; + INFOPLIST_FILE = "Target Support Files/Pods-Anyway/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MODULEMAP_FILE = "Target Support Files/Alamofire/Alamofire.modulemap"; - MTL_ENABLE_DEBUG_INFO = YES; - PRODUCT_NAME = Alamofire; + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-Anyway/Pods-Anyway.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = iphoneos; SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = Debug; + name = Release; }; /* End XCBuildConfiguration section */ @@ -2165,8 +2250,8 @@ 0E68B89957875F63C749237ADEA3D249 /* Build configuration list for PBXNativeTarget "Realm" */ = { isa = XCConfigurationList; buildConfigurations = ( - 93B9DC730F67DD5F78206546BEE74B08 /* Debug */, - 0AA6BC82AD7877E120FF86C425DF245B /* Release */, + 29A7CBD6C628E1F2F5D9B17B681B11A3 /* Debug */, + 04BB07A9A550A0156EBBF4CFA8A3E091 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -2174,8 +2259,8 @@ 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */ = { isa = XCConfigurationList; buildConfigurations = ( - 015A368F878AC3E2CEAE21DDE8026304 /* Debug */, - 44CDBB6D11DE06DB64D6268622BDC47E /* Release */, + 1EE19F5DD95931924296F637BF18BD8F /* Debug */, + F4568DEE257655D290C2B9CEAB37C934 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -2183,44 +2268,44 @@ 419E5D95491847CD79841B971A8A3277 /* Build configuration list for PBXNativeTarget "Alamofire" */ = { isa = XCConfigurationList; buildConfigurations = ( - F383079BFBF927813EA3613CFB679FDE /* Debug */, - 0A29B6F510198AF64EFD762EF6FA97A5 /* Release */, + EA0ADC75B20BB62958101306980460E6 /* Debug */, + A18C9CC7E37563F2891E7DC87F2DE6CE /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 50C8E1EFA0C5DC6AD96DBEE0EFA14308 /* Build configuration list for PBXNativeTarget "Eureka" */ = { + 61735792023D6C269F554591337E1350 /* Build configuration list for PBXNativeTarget "RealmSwift" */ = { isa = XCConfigurationList; buildConfigurations = ( - B4EFF44FB30498779D7B363BDACF73AC /* Debug */, - 5B736E2F72E724B0A86BF9B011C2B6A3 /* Release */, + 4ED57640784516C1F626212129750E2B /* Debug */, + 49D7EC8F5AACF86F11626E64C9D1BEC3 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 61735792023D6C269F554591337E1350 /* Build configuration list for PBXNativeTarget "RealmSwift" */ = { + 9EB26A0A288BADB3E8B250C617F917E3 /* Build configuration list for PBXNativeTarget "Pods-Anyway" */ = { isa = XCConfigurationList; buildConfigurations = ( - C1E6E755215ED021B069461A42EE2734 /* Debug */, - BF96FC4E417B90B65C31D6E388601B00 /* Release */, + B084585D96EFD33BB3E515F7EDD9E699 /* Debug */, + F5183957ECD100E68CDD3924E4D1125F /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 9EB26A0A288BADB3E8B250C617F917E3 /* Build configuration list for PBXNativeTarget "Pods-Anyway" */ = { + B75E102F0ECE2BBBDA28EF8D81A27D60 /* Build configuration list for PBXNativeTarget "SwiftyJSON" */ = { isa = XCConfigurationList; buildConfigurations = ( - 0EA4EB270ABBD0D8945C050B09AD6F73 /* Debug */, - 448C7F3969F6BC7730858C439F6B001F /* Release */, + 69C9940146907A07D5E6B8D2A6FC8749 /* Debug */, + AD619FDDE8FC7869F438098A8DD0E440 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - B75E102F0ECE2BBBDA28EF8D81A27D60 /* Build configuration list for PBXNativeTarget "SwiftyJSON" */ = { + BEEEC40F253F5FFDFB398B3A678C5BAB /* Build configuration list for PBXNativeTarget "Eureka" */ = { isa = XCConfigurationList; buildConfigurations = ( - 8569249F085AB6D7BCF921BDCE510CA0 /* Debug */, - 26223D1FCCF4EA26300D8AC8D8D0D6CC /* Release */, + 349F0ABA411C1CC4D2026225F5759B35 /* Debug */, + CE400855FA0980198C0B2AE3C94E89C2 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; diff --git a/Pods/Realm/LICENSE b/Pods/Realm/LICENSE index 8a84f2b..57a0e0b 100644 --- a/Pods/Realm/LICENSE +++ b/Pods/Realm/LICENSE @@ -4,7 +4,7 @@ TABLE OF CONTENTS 2. Realm Components 3. Export Compliance -------------------------------------------------------------------------------- +1. ------------------------------------------------------------------------------- Apache License Version 2.0, January 2004 @@ -181,17 +181,19 @@ TABLE OF CONTENTS incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. +2. ------------------------------------------------------------------------------- + REALM COMPONENTS This software contains components with separate copyright and license terms. Your use of these components is subject to the terms and conditions of the following licenses. -For the Realm Core component +For the Realm Platform Extensions component - Realm Core Binary License + Realm Platform Extensions License - Copyright (c) 2011-2016 Realm Inc All rights reserved + Copyright (c) 2011-2017 Realm Inc All rights reserved Redistribution and use in binary form, with or without modification, is permitted provided that the following conditions are met: @@ -222,6 +224,8 @@ For the Realm Core component ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +3. ------------------------------------------------------------------------------- + EXPORT COMPLIANCE You understand that the Software may contain cryptographic functions that may be diff --git a/Pods/Realm/README.md b/Pods/Realm/README.md index 0a95cb9..ae255b2 100644 --- a/Pods/Realm/README.md +++ b/Pods/Realm/README.md @@ -7,7 +7,7 @@ This repository holds the source code for the iOS, macOS, tvOS & watchOS version * **Mobile-first:** Realm is the first database built from the ground up to run directly inside phones, tablets and wearables. * **Simple:** Data is directly [exposed as objects](https://realm.io/docs/objc/latest/#models) and [queryable by code](https://realm.io/docs/objc/latest/#queries), removing the need for ORM's riddled with performance & maintenance issues. Most of our users pick it up intuitively, getting simple apps up & running in minutes. -* **Modern:** Realm supports relationships, generics, vectorization and even Swift. +* **Modern:** Realm supports relationships, generics, vectorization and Swift. * **Fast:** Realm is faster than even raw SQLite on common operations, while maintaining an extremely rich feature set. ## Getting Started @@ -31,7 +31,7 @@ The API reference is located at [realm.io/docs/swift/latest/api](https://realm.i - **Need help with your code?**: Look for previous questions on the [#realm tag](https://stackoverflow.com/questions/tagged/realm?sort=newest) — or [ask a new question](https://stackoverflow.com/questions/ask?tags=realm). We actively monitor & answer questions on SO! - **Have a bug to report?** [Open an issue](https://github.com/realm/realm-cocoa/issues/new). If possible, include the version of Realm, a full log, the Realm file, and a project that shows the issue. - **Have a feature request?** [Open an issue](https://github.com/realm/realm-cocoa/issues/new). Tell us what the feature should do, and why you want the feature. -- Sign up for our [**Community Newsletter**](https://www2.realm.io/l/210132/2016-12-05/fy9m) to get regular tips, learn about other use-cases and get alerted of blogposts and tutorials about Realm. +- Sign up for our [**Community Newsletter**](https://realm.io/realm-news-subscribe) to get regular tips, learn about other use-cases and get alerted of blogposts and tutorials about Realm. ## Building Realm @@ -67,7 +67,7 @@ not eligible to receive the product under U.S. law.** ## Feedback -**_If you use Realm and are happy with it, all we ask is that you please consider sending out a tweet mentioning [@realm](https://twitter.com/realm) or email [help@realm.io](mailto:help@realm.io) to share your thoughts!_** +**_If you use Realm and are happy with it, all we ask is that you please consider sending out a tweet mentioning [@realm](https://twitter.com/realm) to share your thoughts!_** **_And if you don't like it, please let us know what you would like improved, so we can fix it!_** diff --git a/Pods/Realm/Realm/NSError+RLMSync.m b/Pods/Realm/Realm/NSError+RLMSync.m index 20938a7..e8c2590 100644 --- a/Pods/Realm/Realm/NSError+RLMSync.m +++ b/Pods/Realm/Realm/NSError+RLMSync.m @@ -22,9 +22,13 @@ @implementation NSError (RLMSync) -- (void(^)(void))rlmSync_clientResetBlock { - if (self.domain == RLMSyncErrorDomain && self.code == RLMSyncErrorClientResetError) { - return self.userInfo[kRLMSyncInitiateClientResetBlockKey]; +- (RLMSyncErrorActionToken *)rlmSync_errorActionToken { + if (self.domain != RLMSyncErrorDomain) { + return nil; + } + if (self.code == RLMSyncErrorClientResetError + || self.code == RLMSyncErrorPermissionDeniedError) { + return (RLMSyncErrorActionToken *)self.userInfo[kRLMSyncErrorActionTokenKey]; } return nil; } diff --git a/Pods/Realm/Realm/ObjectStore/src/binding_callback_thread_observer.cpp b/Pods/Realm/Realm/ObjectStore/src/binding_callback_thread_observer.cpp new file mode 100644 index 0000000..0c388d0 --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/binding_callback_thread_observer.cpp @@ -0,0 +1,23 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2017 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "binding_callback_thread_observer.hpp" + +namespace realm { +BindingCallbackThreadObserver* g_binding_callback_thread_observer = nullptr; +} diff --git a/Pods/Realm/Realm/ObjectStore/src/collection_notifications.cpp b/Pods/Realm/Realm/ObjectStore/src/collection_notifications.cpp index 165021d..03fad73 100644 --- a/Pods/Realm/Realm/ObjectStore/src/collection_notifications.cpp +++ b/Pods/Realm/Realm/ObjectStore/src/collection_notifications.cpp @@ -23,7 +23,7 @@ using namespace realm; using namespace realm::_impl; -NotificationToken::NotificationToken(std::shared_ptr<_impl::CollectionNotifier> notifier, size_t token) +NotificationToken::NotificationToken(std::shared_ptr<_impl::CollectionNotifier> notifier, uint64_t token) : m_notifier(std::move(notifier)), m_token(token) { } diff --git a/Pods/Realm/Realm/ObjectStore/src/impl/apple/keychain_helper.cpp b/Pods/Realm/Realm/ObjectStore/src/impl/apple/keychain_helper.cpp index b12128d..8c9000c 100644 --- a/Pods/Realm/Realm/ObjectStore/src/impl/apple/keychain_helper.cpp +++ b/Pods/Realm/Realm/ObjectStore/src/impl/apple/keychain_helper.cpp @@ -18,7 +18,6 @@ #include "impl/apple/keychain_helper.hpp" -#include "util/format.hpp" #include #include @@ -29,6 +28,7 @@ using realm::util::CFPtr; using realm::util::adoptCF; +using realm::util::retainCF; namespace realm { namespace keychain { @@ -36,10 +36,11 @@ namespace keychain { KeychainAccessException::KeychainAccessException(int32_t error_code) : std::runtime_error(util::format("Keychain returned unexpected status code: %1", error_code)) { } -CFPtr convert_string(const std::string& string); -CFPtr build_search_dictionary(const std::string& account, const std::string& service, - util::Optional group); +namespace { +constexpr size_t key_size = 64; + +#if !TARGET_IPHONE_SIMULATOR CFPtr convert_string(const std::string& string) { auto result = adoptCF(CFStringCreateWithBytes(nullptr, reinterpret_cast(string.data()), @@ -49,67 +50,94 @@ CFPtr convert_string(const std::string& string) } return result; } +#endif -CFPtr build_search_dictionary(const std::string& account, const std::string& service, +CFPtr build_search_dictionary(CFStringRef account, CFStringRef service, __unused util::Optional group) { auto d = adoptCF(CFDictionaryCreateMutable(nullptr, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); - if (!d) { + if (!d) throw std::bad_alloc(); - } + CFDictionaryAddValue(d.get(), kSecClass, kSecClassGenericPassword); CFDictionaryAddValue(d.get(), kSecReturnData, kCFBooleanTrue); CFDictionaryAddValue(d.get(), kSecAttrAccessible, kSecAttrAccessibleAlways); - CFDictionaryAddValue(d.get(), kSecAttrAccount, convert_string(account).get()); - CFDictionaryAddValue(d.get(), kSecAttrService, convert_string(service).get()); -#if TARGET_IPHONE_SIMULATOR -#else - if (group) { + CFDictionaryAddValue(d.get(), kSecAttrAccount, account); + CFDictionaryAddValue(d.get(), kSecAttrService, service); +#if !TARGET_IPHONE_SIMULATOR + if (group) CFDictionaryAddValue(d.get(), kSecAttrAccessGroup, convert_string(*group).get()); - } #endif return d; } -std::vector metadata_realm_encryption_key() +/// Get the encryption key for a given service, returning it only if it exists. +util::Optional> get_key(CFStringRef account, CFStringRef service) { - const size_t key_size = 64; - const std::string service = "io.realm.sync.keychain"; - const std::string account = "metadata"; - auto search_dictionary = build_search_dictionary(account, service, none); CFDataRef retained_key_data; if (OSStatus status = SecItemCopyMatching(search_dictionary.get(), (CFTypeRef *)&retained_key_data)) { - if (status != errSecItemNotFound) { - throw KeychainAccessException(status); - } - - // Key was not found. Generate a new key, store it, and return it. - std::vector key(key_size); - arc4random_buf(key.data(), key_size); - auto key_data = adoptCF(CFDataCreate(nullptr, reinterpret_cast(key.data()), key_size)); - if (!key_data) { - throw std::bad_alloc(); - } - - CFDictionaryAddValue(search_dictionary.get(), kSecValueData, key_data.get()); - if (OSStatus status = SecItemAdd(search_dictionary.get(), nullptr)) { + if (status != errSecItemNotFound) throw KeychainAccessException(status); - } - return key; + // Key was not found. + return none; } - CFPtr key_data = adoptCF(retained_key_data); // Key was previously stored. Extract it. - if (key_size != CFDataGetLength(key_data.get())) { + CFPtr key_data = adoptCF(retained_key_data); + if (key_size != CFDataGetLength(key_data.get())) throw std::runtime_error("Password stored in keychain was not expected size."); - } auto key_bytes = reinterpret_cast(CFDataGetBytePtr(key_data.get())); return std::vector(key_bytes, key_bytes + key_size); } - + +void set_key(const std::vector& key, CFStringRef account, CFStringRef service) +{ + auto search_dictionary = build_search_dictionary(account, service, none); + auto key_data = adoptCF(CFDataCreate(nullptr, reinterpret_cast(key.data()), key_size)); + if (!key_data) + throw std::bad_alloc(); + + CFDictionaryAddValue(search_dictionary.get(), kSecValueData, key_data.get()); + if (OSStatus status = SecItemAdd(search_dictionary.get(), nullptr)) + throw KeychainAccessException(status); } + +} // anonymous namespace + +std::vector metadata_realm_encryption_key(bool check_legacy_service) +{ + CFStringRef account = CFSTR("metadata"); + CFStringRef legacy_service = CFSTR("io.realm.sync.keychain"); + + CFPtr service; + if (CFStringRef bundle_id = CFBundleGetIdentifier(CFBundleGetMainBundle())) + service = adoptCF(CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ - Realm Sync Metadata Key"), bundle_id)); + else { + service = retainCF(legacy_service); + check_legacy_service = false; + } + + // Try retrieving the key. + if (auto existing_key = get_key(account, service.get())) { + return *existing_key; + } else if (check_legacy_service) { + // See if there's a key stored using the legacy shared keychain item. + if (auto existing_legacy_key = get_key(account, legacy_service)) { + // If so, copy it to the per-app keychain item before returning it. + set_key(*existing_legacy_key, account, service.get()); + return *existing_legacy_key; + } + } + // Make a completely new key. + std::vector key(key_size); + arc4random_buf(key.data(), key_size); + set_key(key, account, service.get()); + return key; } + +} // keychain +} // realm diff --git a/Pods/Realm/Realm/ObjectStore/src/impl/collection_change_builder.cpp b/Pods/Realm/Realm/ObjectStore/src/impl/collection_change_builder.cpp index a225764..40b5754 100644 --- a/Pods/Realm/Realm/ObjectStore/src/impl/collection_change_builder.cpp +++ b/Pods/Realm/Realm/ObjectStore/src/impl/collection_change_builder.cpp @@ -77,7 +77,6 @@ void CollectionChangeBuilder::merge(CollectionChangeBuilder&& c) old.to = it->to; *it = c.moves.back(); c.moves.pop_back(); - ++it; return false; } @@ -195,6 +194,8 @@ void CollectionChangeBuilder::for_each_col(Func&& f) void CollectionChangeBuilder::insert(size_t index, size_t count, bool track_moves) { + REALM_ASSERT(count != 0); + for_each_col([=](auto& col) { col.shift_for_insert_at(index, count); }); if (!track_moves) return; @@ -203,7 +204,7 @@ void CollectionChangeBuilder::insert(size_t index, size_t count, bool track_move for (auto& move : moves) { if (move.to >= index) - ++move.to; + move.to += count; } if (m_move_mapping.empty()) diff --git a/Pods/Realm/Realm/ObjectStore/src/impl/collection_notifier.cpp b/Pods/Realm/Realm/ObjectStore/src/impl/collection_notifier.cpp index 0191d3d..8fa94d3 100644 --- a/Pods/Realm/Realm/ObjectStore/src/impl/collection_notifier.cpp +++ b/Pods/Realm/Realm/ObjectStore/src/impl/collection_notifier.cpp @@ -31,6 +31,9 @@ std::function CollectionNotifier::get_modification_checker(TransactionChangeInfo const& info, Table const& root_table) { + if (info.schema_changed) + set_table(root_table); + // First check if any of the tables accessible from the root table were // actually modified. This can be false if there were only insertions, or // deletions which were not linked to by any row in the linking table @@ -41,6 +44,10 @@ CollectionNotifier::get_modification_checker(TransactionChangeInfo const& info, if (!any_of(begin(m_related_tables), end(m_related_tables), table_modified)) { return [](size_t) { return false; }; } + if (m_related_tables.size() == 1) { + auto& modifications = info.tables[m_related_tables[0].table_ndx].modifications; + return [&](size_t row) { return modifications.contains(row); }; + } return DeepChangeChecker(info, root_table, m_related_tables); } @@ -48,6 +55,8 @@ CollectionNotifier::get_modification_checker(TransactionChangeInfo const& info, void DeepChangeChecker::find_related_tables(std::vector& out, Table const& table) { auto table_ndx = table.get_index_in_group(); + if (table_ndx == npos) + return; if (any_of(begin(out), end(out), [=](auto& tbl) { return tbl.table_ndx == table_ndx; })) return; @@ -90,20 +99,24 @@ bool DeepChangeChecker::check_outgoing_links(size_t table_ndx, // Check if we're already checking if the destination of the link is // modified, and if not add it to the stack auto already_checking = [&](size_t col) { - for (auto p = m_current_path.begin(); p < m_current_path.begin() + depth; ++p) { - if (p->table == table_ndx && p->row == row_ndx && p->col == col) - return true; + auto end = m_current_path.begin() + depth; + auto match = std::find_if(m_current_path.begin(), end, [&](auto& p) { + return p.table == table_ndx && p.row == row_ndx && p.col == col; + }); + if (match != end) { + for (; match < end; ++match) match->depth_exceeded = true; + return true; } m_current_path[depth] = {table_ndx, row_ndx, col, false}; return false; }; - for (auto const& link : it->links) { + auto linked_object_changed = [&](OutgoingLink const& link) { if (already_checking(link.col_ndx)) - continue; + return false; if (!link.is_list) { if (table.is_null_link(link.col_ndx, row_ndx)) - continue; + return false; auto dst = table.get_link(link.col_ndx, row_ndx); return check_row(*table.get_link_target(link.col_ndx), dst, depth + 1); } @@ -115,9 +128,10 @@ bool DeepChangeChecker::check_outgoing_links(size_t table_ndx, if (check_row(target, dst, depth + 1)) return true; } - } + return false; + }; - return false; + return std::any_of(begin(it->links), end(it->links), linked_object_changed); } bool DeepChangeChecker::check_row(Table const& table, size_t idx, size_t depth) @@ -126,7 +140,7 @@ bool DeepChangeChecker::check_row(Table const& table, size_t idx, size_t depth) if (depth >= m_current_path.size()) { // Don't mark any of the intermediate rows checked along the path as // not modified, as a search starting from them might hit a modification - for (size_t i = 1; i < m_current_path.size(); ++i) + for (size_t i = 0; i < m_current_path.size(); ++i) m_current_path[i].depth_exceeded = true; return false; } @@ -141,7 +155,7 @@ bool DeepChangeChecker::check_row(Table const& table, size_t idx, size_t depth) return false; bool ret = check_outgoing_links(table_ndx, table, idx, depth); - if (!ret && !m_current_path[depth].depth_exceeded) + if (!ret && (depth == 0 || !m_current_path[depth - 1].depth_exceeded)) m_not_modified[table_ndx].add(idx); return ret; } @@ -155,7 +169,7 @@ bool DeepChangeChecker::operator()(size_t ndx) CollectionNotifier::CollectionNotifier(std::shared_ptr realm) : m_realm(std::move(realm)) -, m_sg_version(Realm::Internal::get_shared_group(*m_realm).get_version_of_current_transaction()) +, m_sg_version(Realm::Internal::get_shared_group(*m_realm)->get_version_of_current_transaction()) { } @@ -166,22 +180,12 @@ CollectionNotifier::~CollectionNotifier() unregister(); } -size_t CollectionNotifier::add_callback(CollectionChangeCallback callback) +uint64_t CollectionNotifier::add_callback(CollectionChangeCallback callback) { m_realm->verify_thread(); - auto next_token = [=] { - size_t token = 0; - for (auto& callback : m_callbacks) { - if (token <= callback.token) { - token = callback.token + 1; - } - } - return token; - }; - std::lock_guard lock(m_callback_mutex); - auto token = next_token(); + auto token = m_next_token++; m_callbacks.push_back({std::move(callback), {}, {}, token, false, false}); if (m_callback_index == npos) { // Don't need to wake up if we're already sending notifications Realm::Internal::get_coordinator(*m_realm).wake_up_notifier_worker(); @@ -190,7 +194,7 @@ size_t CollectionNotifier::add_callback(CollectionChangeCallback callback) return token; } -void CollectionNotifier::remove_callback(size_t token) +void CollectionNotifier::remove_callback(uint64_t token) { // the callback needs to be destroyed after releasing the lock as destroying // it could cause user code to be called @@ -203,9 +207,11 @@ void CollectionNotifier::remove_callback(size_t token) } size_t idx = distance(begin(m_callbacks), it); - if (m_callback_index != npos && m_callback_index >= idx) { - --m_callback_index; + if (m_callback_index != npos) { + if (m_callback_index >= idx) + --m_callback_index; } + --m_callback_count; old = std::move(*it); m_callbacks.erase(it); @@ -214,7 +220,7 @@ void CollectionNotifier::remove_callback(size_t token) } } -void CollectionNotifier::suppress_next_notification(size_t token) +void CollectionNotifier::suppress_next_notification(uint64_t token) { { std::lock_guard lock(m_realm_mutex); @@ -230,7 +236,7 @@ void CollectionNotifier::suppress_next_notification(size_t token) } } -std::vector::iterator CollectionNotifier::find_callback(size_t token) +std::vector::iterator CollectionNotifier::find_callback(uint64_t token) { REALM_ASSERT(m_error || m_callbacks.size() > 0); @@ -329,18 +335,21 @@ void CollectionNotifier::after_advance() void CollectionNotifier::deliver_error(std::exception_ptr error) { - for_each_callback([&](auto& lock, auto& callback) { + // Don't complain about double-unregistering callbacks + m_error = true; + + m_callback_count = m_callbacks.size(); + for_each_callback([this, &error](auto& lock, auto& callback) { // acquire a local reference to the callback so that removing the // callback from within it can't result in a dangling pointer - auto cb = callback.fn; + auto cb = std::move(callback.fn); + auto token = callback.token; lock.unlock(); cb.error(error); - }); - // Remove all the callbacks as we never need to call anything ever again - // after delivering an error - m_callbacks.clear(); - m_error = true; + // We never want to call the callback again after this, so just remove it + this->remove_callback(token); + }); } bool CollectionNotifier::is_for_realm(Realm& realm) const noexcept @@ -356,6 +365,7 @@ bool CollectionNotifier::package_for_delivery() std::lock_guard l(m_callback_mutex); for (auto& callback : m_callbacks) callback.changes_to_deliver = std::move(callback.accumulated_changes).finalize(); + m_callback_count = m_callbacks.size(); return true; } @@ -363,7 +373,8 @@ template void CollectionNotifier::for_each_callback(Fn&& fn) { std::unique_lock callback_lock(m_callback_mutex); - for (++m_callback_index; m_callback_index < m_callbacks.size(); ++m_callback_index) { + REALM_ASSERT_DEBUG(m_callback_count <= m_callbacks.size()); + for (++m_callback_index; m_callback_index < m_callback_count; ++m_callback_index) { fn(callback_lock, m_callbacks[m_callback_index]); if (!callback_lock.owns_lock()) callback_lock.lock(); @@ -387,6 +398,11 @@ void CollectionNotifier::detach() m_sg = nullptr; } +SharedGroup& CollectionNotifier::source_shared_group() +{ + return *Realm::Internal::get_shared_group(*m_realm); +} + void CollectionNotifier::add_changes(CollectionChangeBuilder change) { std::lock_guard lock(m_callback_mutex); diff --git a/Pods/Realm/Realm/ObjectStore/src/impl/list_notifier.cpp b/Pods/Realm/Realm/ObjectStore/src/impl/list_notifier.cpp index beec1d5..a2b459c 100644 --- a/Pods/Realm/Realm/ObjectStore/src/impl/list_notifier.cpp +++ b/Pods/Realm/Realm/ObjectStore/src/impl/list_notifier.cpp @@ -30,9 +30,7 @@ ListNotifier::ListNotifier(LinkViewRef lv, std::shared_ptr realm) , m_prev_size(lv->size()) { set_table(lv->get_target_table()); - - auto& sg = Realm::Internal::get_shared_group(*get_realm()); - m_lv_handover = sg.export_linkview_for_handover(lv); + m_lv_handover = source_shared_group().export_linkview_for_handover(lv); } void ListNotifier::release_data() noexcept @@ -63,17 +61,9 @@ bool ListNotifier::do_add_required_change_info(TransactionChangeInfo& info) return false; // origin row was deleted after the notification was added } - // Find the lv's column, since that isn't tracked directly auto& table = m_lv->get_origin_table(); size_t row_ndx = m_lv->get_origin_row_index(); - size_t col_ndx = not_found; - for (size_t i = 0, count = table.get_column_count(); i != count; ++i) { - if (table.get_column_type(i) == type_LinkList && table.get_linklist(i, row_ndx) == m_lv) { - col_ndx = i; - break; - } - } - REALM_ASSERT(col_ndx != not_found); + size_t col_ndx = find_container_column(table, row_ndx, m_lv, type_LinkList, &Table::get_linklist); info.lists.push_back({table.get_index_in_group(), row_ndx, col_ndx, &m_change}); m_info = &info; diff --git a/Pods/Realm/Realm/ObjectStore/src/impl/object_notifier.cpp b/Pods/Realm/Realm/ObjectStore/src/impl/object_notifier.cpp index bbc65b8..cb31065 100644 --- a/Pods/Realm/Realm/ObjectStore/src/impl/object_notifier.cpp +++ b/Pods/Realm/Realm/ObjectStore/src/impl/object_notifier.cpp @@ -29,8 +29,7 @@ ObjectNotifier::ObjectNotifier(Row const& row, std::shared_ptr realm) REALM_ASSERT(row.get_table()); set_table(*row.get_table()); - auto& sg = Realm::Internal::get_shared_group(*get_realm()); - m_handover = sg.export_for_handover(row); + m_handover = source_shared_group().export_for_handover(row); } void ObjectNotifier::release_data() noexcept diff --git a/Pods/Realm/Realm/ObjectStore/src/impl/primitive_list_notifier.cpp b/Pods/Realm/Realm/ObjectStore/src/impl/primitive_list_notifier.cpp new file mode 100644 index 0000000..9be9acb --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/impl/primitive_list_notifier.cpp @@ -0,0 +1,99 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2017 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "impl/primitive_list_notifier.hpp" + +#include "shared_realm.hpp" + +#include + +using namespace realm; +using namespace realm::_impl; + +PrimitiveListNotifier::PrimitiveListNotifier(TableRef table, std::shared_ptr realm) +: CollectionNotifier(std::move(realm)) +, m_prev_size(table->size()) +{ + set_table(*table->get_parent_table()); + m_table_handover = source_shared_group().export_table_for_handover(table); +} + +void PrimitiveListNotifier::release_data() noexcept +{ + m_table.reset(); +} + +void PrimitiveListNotifier::do_attach_to(SharedGroup& sg) +{ + REALM_ASSERT(m_table_handover); + REALM_ASSERT(!m_table); + m_table = sg.import_table_from_handover(std::move(m_table_handover)); +} + +void PrimitiveListNotifier::do_detach_from(SharedGroup& sg) +{ + REALM_ASSERT(!m_table_handover); + if (m_table) { + m_table_handover = sg.export_table_for_handover(m_table); + m_table = {}; + } +} + +bool PrimitiveListNotifier::do_add_required_change_info(TransactionChangeInfo& info) +{ + REALM_ASSERT(!m_table_handover); + if (!m_table || !m_table->is_attached()) { + return false; // origin row was deleted after the notification was added + } + + auto& table = *m_table->get_parent_table(); + size_t row_ndx = m_table->get_parent_row_index(); + size_t col_ndx = find_container_column(table, row_ndx, m_table, type_Table, &Table::get_subtable); + info.lists.push_back({table.get_index_in_group(), row_ndx, col_ndx, &m_change}); + + m_info = &info; + return true; +} + +void PrimitiveListNotifier::run() +{ + if (!m_table || !m_table->is_attached()) { + // Table was deleted entirely, so report all of the rows being removed + // if this is the first run after that + if (m_prev_size) { + m_change.deletions.set(m_prev_size); + m_prev_size = 0; + } + else { + m_change = {}; + } + return; + } + + if (!m_change.deletions.empty() && m_change.deletions.begin()->second == std::numeric_limits::max()) { + // Table was cleared, so set the deletions to the actual previous size + m_change.deletions.set(m_prev_size); + } + + m_prev_size = m_table->size(); +} + +void PrimitiveListNotifier::do_prepare_handover(SharedGroup&) +{ + add_changes(std::move(m_change)); +} diff --git a/Pods/Realm/Realm/ObjectStore/src/impl/realm_coordinator.cpp b/Pods/Realm/Realm/ObjectStore/src/impl/realm_coordinator.cpp index b85c69a..343193a 100644 --- a/Pods/Realm/Realm/ObjectStore/src/impl/realm_coordinator.cpp +++ b/Pods/Realm/Realm/ObjectStore/src/impl/realm_coordinator.cpp @@ -28,6 +28,7 @@ #include "schema.hpp" #if REALM_ENABLE_SYNC +#include "sync/impl/work_queue.hpp" #include "sync/sync_config.hpp" #include "sync/sync_manager.hpp" #include "sync/sync_session.hpp" @@ -81,7 +82,19 @@ void RealmCoordinator::create_sync_session() if (m_sync_session) return; - m_sync_session = SyncManager::shared().get_session(m_config.path, *m_config.sync_config); + if (!m_config.encryption_key.empty() && !m_config.sync_config->realm_encryption_key) { + throw std::logic_error("A realm encryption key was specified in Realm::Config but not in SyncConfig"); + } else if (m_config.sync_config->realm_encryption_key && m_config.encryption_key.empty()) { + throw std::logic_error("A realm encryption key was specified in SyncConfig but not in Realm::Config"); + } else if (m_config.sync_config->realm_encryption_key && + !std::equal(m_config.sync_config->realm_encryption_key->begin(), m_config.sync_config->realm_encryption_key->end(), + m_config.encryption_key.begin(), m_config.encryption_key.end())) { + throw std::logic_error("The realm encryption key specified in SyncConfig does not match the one in Realm::Config"); + } + + auto sync_config = *m_config.sync_config; + sync_config.validate_sync_history = false; + m_sync_session = SyncManager::shared().get_session(m_config.path, sync_config); std::weak_ptr weak_self = shared_from_this(); SyncSession::Internal::set_sync_transact_callback(*m_sync_session, @@ -93,9 +106,6 @@ void RealmCoordinator::create_sync_session() self->m_notifier->notify_others(); } }); - if (m_config.sync_config->error_handler) { - SyncSession::Internal::set_error_handler(*m_sync_session, m_config.sync_config->error_handler); - } #endif } @@ -103,15 +113,21 @@ void RealmCoordinator::set_config(const Realm::Config& config) { if (config.encryption_key.data() && config.encryption_key.size() != 64) throw InvalidEncryptionKeyException(); - if (config.schema_mode == SchemaMode::ReadOnly && config.sync_config) - throw std::logic_error("Synchronized Realms cannot be opened in read-only mode"); + if (config.schema_mode == SchemaMode::Immutable && config.sync_config) + throw std::logic_error("Synchronized Realms cannot be opened in immutable mode"); if (config.schema_mode == SchemaMode::Additive && config.migration_function) throw std::logic_error("Realms opened in Additive-only schema mode do not use a migration function"); - if (config.schema_mode == SchemaMode::ReadOnly && config.migration_function) + if (config.schema_mode == SchemaMode::Immutable && config.migration_function) + throw std::logic_error("Realms opened in immutable mode do not use a migration function"); + if (config.schema_mode == SchemaMode::ReadOnlyAlternative && config.migration_function) throw std::logic_error("Realms opened in read-only mode do not use a migration function"); + if (config.schema_mode == SchemaMode::Immutable && config.initialization_function) + throw std::logic_error("Realms opened in immutable mode do not use an initialization function"); + if (config.schema_mode == SchemaMode::ReadOnlyAlternative && config.initialization_function) + throw std::logic_error("Realms opened in read-only mode do not use an initialization function"); if (config.schema && config.schema_version == ObjectStore::NotVersioned) throw std::logic_error("A schema version must be specified when the schema is specified"); - if (!config.realm_data.is_null() && (!config.read_only() || !config.in_memory)) + if (!config.realm_data.is_null() && (!config.immutable() || !config.in_memory)) throw std::logic_error("In-memory realms initialized from memory buffers can only be opened in read-only mode"); if (!config.realm_data.is_null() && !config.path.empty()) throw std::logic_error("Specifying both memory buffer and path is invalid"); @@ -126,7 +142,7 @@ void RealmCoordinator::set_config(const Realm::Config& config) m_config = config; } else { - if (m_config.read_only() != config.read_only()) { + if (m_config.immutable() != config.immutable()) { throw MismatchedConfigException("Realm at path '%1' already opened with different read permissions.", config.path); } if (m_config.in_memory != config.in_memory) { @@ -138,6 +154,9 @@ void RealmCoordinator::set_config(const Realm::Config& config) if (m_config.schema_mode != config.schema_mode) { throw MismatchedConfigException("Realm at path '%1' already opened with a different schema mode.", config.path); } + if (config.schema && m_schema_version != ObjectStore::NotVersioned && m_schema_version != config.schema_version) { + throw MismatchedConfigException("Realm at path '%1' already opened with different schema version.", config.path); + } #if REALM_ENABLE_SYNC if (bool(m_config.sync_config) != bool(config.sync_config)) { @@ -148,66 +167,82 @@ void RealmCoordinator::set_config(const Realm::Config& config) if (m_config.sync_config->user != config.sync_config->user) { throw MismatchedConfigException("Realm at path '%1' already opened with different sync user.", config.path); } - if (m_config.sync_config->realm_url != config.sync_config->realm_url) { + if (m_config.sync_config->realm_url() != config.sync_config->realm_url()) { throw MismatchedConfigException("Realm at path '%1' already opened with different sync server URL.", config.path); } + if (m_config.sync_config->transformer != config.sync_config->transformer) { + throw MismatchedConfigException("Realm at path '%1' already opened with different transformer.", config.path); + } + if (m_config.sync_config->realm_encryption_key != config.sync_config->realm_encryption_key) { + throw MismatchedConfigException("Realm at path '%1' already opened with sync session encryption key.", config.path); + } } #endif // Realm::update_schema() handles complaining about schema mismatches } - -#if REALM_ENABLE_SYNC - if (config.sync_config) { - create_sync_session(); - } -#endif } std::shared_ptr RealmCoordinator::get_realm(Realm::Config config) { + // realm must be declared before lock so that the mutex is released before + // we release the strong reference to realm, as Realm's destructor may want + // to acquire the same lock + std::shared_ptr realm; std::unique_lock lock(m_realm_mutex); set_config(config); auto schema = std::move(config.schema); auto migration_function = std::move(config.migration_function); + auto initialization_function = std::move(config.initialization_function); config.schema = {}; - std::shared_ptr realm; if (config.cache) { AnyExecutionContextID execution_context(config.execution_context); for (auto& cached_realm : m_weak_realm_notifiers) { if (!cached_realm.is_cached_for_execution_context(execution_context)) continue; - realm = cached_realm.realm(); // can be null if we jumped in between ref count hitting zero and // unregister_realm() getting the lock - if (realm) - break; + if ((realm = cached_realm.realm())) { + // If the file is uninitialized and was opened without a schema, + // do the normal schema init + if (realm->schema_version() == ObjectStore::NotVersioned) + break; + + // Otherwise if we have a realm schema it needs to be an exact + // match (even having the same properties but in different + // orders isn't good enough) + if (schema && realm->schema() != *schema) + throw MismatchedConfigException("Realm at path '%1' already opened on current thread with different schema.", config.path); + + return realm; + } } } + if (!realm) { + bool should_initialize_notifier = !config.immutable() && config.automatic_change_notifications; realm = Realm::make_shared_realm(std::move(config), shared_from_this()); - if (!config.read_only() && !m_notifier && config.automatic_change_notifications) { + if (!m_notifier && should_initialize_notifier) { try { m_notifier = std::make_unique(*this); } catch (std::system_error const& ex) { - lock.unlock(); - throw RealmFileException(RealmFileException::Kind::AccessError, config.path, ex.code().message(), ""); + throw RealmFileException(RealmFileException::Kind::AccessError, get_path(), ex.code().message(), ""); } } - m_weak_realm_notifiers.emplace_back(realm, m_config.cache); + m_weak_realm_notifiers.emplace_back(realm, realm->config().cache); } + if (realm->config().sync_config) + create_sync_session(); + if (schema) { - auto old_schema_version = m_schema_version; lock.unlock(); - - if (old_schema_version != ObjectStore::NotVersioned && old_schema_version != config.schema_version) - throw MismatchedConfigException("Realm at path '%1' already opened with different schema version.", config.path); - realm->update_schema(std::move(*schema), config.schema_version, std::move(migration_function)); + realm->update_schema(std::move(*schema), config.schema_version, std::move(migration_function), + std::move(initialization_function)); } return realm; @@ -218,18 +253,58 @@ std::shared_ptr RealmCoordinator::get_realm() return get_realm(m_config); } -const Schema* RealmCoordinator::get_schema() const noexcept +bool RealmCoordinator::get_cached_schema(Schema& schema, uint64_t& schema_version, + uint64_t& transaction) const noexcept { - return m_schema_version == uint64_t(-1) ? nullptr : &m_schema; + std::lock_guard lock(m_schema_cache_mutex); + if (!m_cached_schema) + return false; + schema = *m_cached_schema; + schema_version = m_schema_version; + transaction = m_schema_transaction_version_max; + return true; } -void RealmCoordinator::update_schema(Schema const& schema, uint64_t schema_version) +void RealmCoordinator::cache_schema(Schema const& new_schema, uint64_t new_schema_version, + uint64_t transaction_version) { - m_schema = schema; - m_schema_version = schema_version; + std::lock_guard lock(m_schema_cache_mutex); + if (transaction_version < m_schema_transaction_version_max) + return; + if (new_schema.empty() || new_schema_version == ObjectStore::NotVersioned) + return; + + m_cached_schema = new_schema; + m_schema_version = new_schema_version; + m_schema_transaction_version_min = transaction_version; + m_schema_transaction_version_max = transaction_version; } -RealmCoordinator::RealmCoordinator() = default; +void RealmCoordinator::clear_schema_cache_and_set_schema_version(uint64_t new_schema_version) +{ + std::lock_guard lock(m_schema_cache_mutex); + m_cached_schema = util::none; + m_schema_version = new_schema_version; +} + +void RealmCoordinator::advance_schema_cache(uint64_t previous, uint64_t next) +{ + std::lock_guard lock(m_schema_cache_mutex); + if (!m_cached_schema) + return; + REALM_ASSERT(previous <= m_schema_transaction_version_max); + if (next < m_schema_transaction_version_min) + return; + m_schema_transaction_version_min = std::min(previous, m_schema_transaction_version_min); + m_schema_transaction_version_max = std::max(next, m_schema_transaction_version_max); +} + +RealmCoordinator::RealmCoordinator() +#if REALM_ENABLE_SYNC +: m_partial_sync_work_queue(std::make_unique()) +#endif +{ +} RealmCoordinator::~RealmCoordinator() { @@ -321,7 +396,7 @@ void RealmCoordinator::wake_up_notifier_worker() void RealmCoordinator::commit_write(Realm& realm) { - REALM_ASSERT(!m_config.read_only()); + REALM_ASSERT(!m_config.immutable()); REALM_ASSERT(realm.is_in_transaction()); { @@ -330,16 +405,24 @@ void RealmCoordinator::commit_write(Realm& realm) // skip version std::lock_guard l(m_notifier_mutex); - transaction::commit(Realm::Internal::get_shared_group(realm)); + transaction::commit(*Realm::Internal::get_shared_group(realm)); // Don't need to check m_new_notifiers because those don't skip versions bool have_notifiers = std::any_of(m_notifiers.begin(), m_notifiers.end(), [&](auto&& notifier) { return notifier->is_for_realm(realm); }); if (have_notifiers) { - m_notifier_skip_version = Realm::Internal::get_shared_group(realm).get_version_of_current_transaction(); + m_notifier_skip_version = Realm::Internal::get_shared_group(realm)->get_version_of_current_transaction(); } } +#if REALM_ENABLE_SYNC + // Realm could be closed in did_change. So send sync notification first before did_change. + if (m_sync_session) { + auto& sg = Realm::Internal::get_shared_group(realm); + auto version = LangBindHelper::get_version_of_latest_snapshot(*sg); + SyncSession::Internal::nonsync_transact_notify(*m_sync_session, version); + } +#endif if (realm.m_binding_context) { realm.m_binding_context->did_change({}, {}); } @@ -347,13 +430,6 @@ void RealmCoordinator::commit_write(Realm& realm) if (m_notifier) { m_notifier->notify_others(); } -#if REALM_ENABLE_SYNC - if (m_sync_session) { - auto& sg = Realm::Internal::get_shared_group(realm); - auto version = LangBindHelper::get_version_of_latest_snapshot(sg); - SyncSession::Internal::nonsync_transact_notify(*m_sync_session, version); - } -#endif } void RealmCoordinator::pin_version(VersionID versionid) @@ -431,6 +507,7 @@ void RealmCoordinator::clean_up_dead_notifiers() if (m_notifiers.empty() && m_notifier_sg) { REALM_ASSERT_3(m_notifier_sg->get_transact_stage(), ==, SharedGroup::transact_Reading); m_notifier_sg->end_read(); + m_notifier_skip_version = {0, 0}; } } if (swap_remove(m_new_notifiers) && m_advancer_sg) { @@ -599,6 +676,20 @@ void RealmCoordinator::run_async_notifiers() for (auto& notifier : new_notifiers) { notifier->detach(); } + + // We want to advance the non-new notifiers to the same version as the + // new notifiers to avoid having to merge changes from any new + // transaction that happen immediately after this into the new notifier + // changes + version = m_advancer_sg->get_version_of_current_transaction(); + m_advancer_sg->end_read(); + } + else { + // If we have no new notifiers we want to just advance to the latest + // version, but we have to pick a "latest" version while holding the + // notifier lock to avoid advancing over a transaction which should be + // skipped + m_advancer_sg->begin_read(); version = m_advancer_sg->get_version_of_current_transaction(); m_advancer_sg->end_read(); } @@ -614,6 +705,7 @@ void RealmCoordinator::run_async_notifiers() lock.unlock(); if (skip_version.version) { + REALM_ASSERT(!notifiers.empty()); REALM_ASSERT(version >= skip_version); IncrementalChangeInfo change_info(*m_notifier_sg, notifiers); for (auto& notifier : notifiers) @@ -693,8 +785,25 @@ void RealmCoordinator::advance_to_ready(Realm& realm) auto& sg = Realm::Internal::get_shared_group(realm); if (notifiers) { auto version = notifiers.version(); - if (version && *version <= sg.get_version_of_current_transaction()) - return; + if (version) { + auto current_version = sg->get_version_of_current_transaction(); + // Notifications are out of date, so just discard + // This should only happen if begin_read() was used to change the + // read version outside of our control + if (*version < current_version) + return; + // While there is a newer version, notifications are for the current + // version so just deliver them without advancing + if (*version == current_version) { + if (realm.m_binding_context) + realm.m_binding_context->will_send_notifications(); + notifiers.deliver(*sg); + notifiers.after_advance(); + if (realm.m_binding_context) + realm.m_binding_context->did_send_notifications(); + return; + } + } } transaction::advance(sg, realm.m_binding_context.get(), notifiers); @@ -722,11 +831,16 @@ bool RealmCoordinator::advance_to_latest(Realm& realm) std::unique_lock lock(m_notifier_mutex); _impl::NotifierPackage notifiers(m_async_error, notifiers_for_realm(realm), this); lock.unlock(); - notifiers.package_and_wait(sgf::get_version_of_latest_snapshot(sg)); + notifiers.package_and_wait(sgf::get_version_of_latest_snapshot(*sg)); - auto version = sg.get_version_of_current_transaction(); + auto version = sg->get_version_of_current_transaction(); transaction::advance(sg, realm.m_binding_context.get(), notifiers); - return version != sg.get_version_of_current_transaction(); + + // Realm could be closed in the callbacks. + if (realm.is_closed()) + return false; + + return version != sg->get_version_of_current_transaction(); } void RealmCoordinator::promote_to_write(Realm& realm) @@ -750,16 +864,23 @@ void RealmCoordinator::process_available_async(Realm& realm) if (notifiers.empty()) return; + if (realm.m_binding_context) + realm.m_binding_context->will_send_notifications(); + if (auto error = m_async_error) { lock.unlock(); for (auto& notifier : notifiers) notifier->deliver_error(m_async_error); + if (realm.m_binding_context) + realm.m_binding_context->did_send_notifications(); return; } bool in_read = realm.is_in_read_transaction(); auto& sg = Realm::Internal::get_shared_group(realm); - auto version = sg.get_version_of_current_transaction(); + if (!sg) // i.e. the Realm was closed in a callback above + return; + auto version = sg->get_version_of_current_transaction(); auto package = [&](auto& notifier) { return !(notifier->has_run() && (!in_read || notifier->version() == version) && notifier->package_for_delivery()); }; @@ -773,15 +894,26 @@ void RealmCoordinator::process_available_async(Realm& realm) // Skip delivering if the Realm isn't in a read transaction if (in_read) { for (auto& notifier : notifiers) - notifier->deliver(sg); + notifier->deliver(*sg); } // but still call the change callbacks for (auto& notifier : notifiers) notifier->after_advance(); + + if (realm.m_binding_context) + realm.m_binding_context->did_send_notifications(); } void RealmCoordinator::set_transaction_callback(std::function fn) { + create_sync_session(); m_transaction_callback = std::move(fn); } + +#if REALM_ENABLE_SYNC +partial_sync::WorkQueue& RealmCoordinator::partial_sync_work_queue() +{ + return *m_partial_sync_work_queue; +} +#endif diff --git a/Pods/Realm/Realm/ObjectStore/src/impl/results_notifier.cpp b/Pods/Realm/Realm/ObjectStore/src/impl/results_notifier.cpp index 36153d9..4a711d3 100644 --- a/Pods/Realm/Realm/ObjectStore/src/impl/results_notifier.cpp +++ b/Pods/Realm/Realm/ObjectStore/src/impl/results_notifier.cpp @@ -18,6 +18,8 @@ #include "impl/results_notifier.hpp" +#include "shared_realm.hpp" + using namespace realm; using namespace realm::_impl; @@ -28,9 +30,8 @@ ResultsNotifier::ResultsNotifier(Results& target) { Query q = target.get_query(); set_table(*q.get_table()); - m_query_handover = Realm::Internal::get_shared_group(*get_realm()).export_for_handover(q, MutableSourcePayload::Move); - SortDescriptor::generate_patch(target.get_sort(), m_sort_handover); - SortDescriptor::generate_patch(target.get_distinct(), m_distinct_handover); + m_query_handover = source_shared_group().export_for_handover(q, MutableSourcePayload::Move); + DescriptorOrdering::generate_patch(target.get_descriptor_ordering(), m_ordering_handover); } void ResultsNotifier::target_results_moved(Results& old_target, Results& new_target) @@ -72,10 +73,22 @@ bool ResultsNotifier::do_add_required_change_info(TransactionChangeInfo& info) REALM_ASSERT(m_query); m_info = &info; - auto table_ndx = m_query->get_table()->get_index_in_group(); - if (info.table_moves_needed.size() <= table_ndx) - info.table_moves_needed.resize(table_ndx + 1); - info.table_moves_needed[table_ndx] = true; + auto& table = *m_query->get_table(); + if (!table.is_attached()) + return false; + + auto table_ndx = table.get_index_in_group(); + if (table_ndx == npos) { // is a subtable + auto& parent = *table.get_parent_table(); + size_t row_ndx = table.get_parent_row_index(); + size_t col_ndx = find_container_column(parent, row_ndx, &table, type_Table, &Table::get_subtable); + info.lists.push_back({parent.get_index_in_group(), row_ndx, col_ndx, &m_changes}); + } + else { // is a top-level table + if (info.table_moves_needed.size() <= table_ndx) + info.table_moves_needed.resize(table_ndx + 1); + info.table_moves_needed[table_ndx] = true; + } return has_run() && have_callbacks(); } @@ -104,8 +117,12 @@ bool ResultsNotifier::need_to_run() void ResultsNotifier::calculate_changes() { size_t table_ndx = m_query->get_table()->get_index_in_group(); - if (has_run()) { - auto changes = table_ndx < m_info->tables.size() ? &m_info->tables[table_ndx] : nullptr; + if (has_run() && have_callbacks()) { + CollectionChangeBuilder* changes = nullptr; + if (table_ndx == npos) + changes = &m_changes; + else if (table_ndx < m_info->tables.size()) + changes = &m_info->tables[table_ndx]; std::vector next_rows; next_rows.reserve(m_tv.size()); @@ -116,16 +133,16 @@ void ResultsNotifier::calculate_changes() if (changes) { auto const& moves = changes->moves; for (auto& idx : m_previous_rows) { - auto it = lower_bound(begin(moves), end(moves), idx, - [](auto const& a, auto b) { return a.from < b; }); - if (it != moves.end() && it->from == idx) - idx = it->to; - else if (changes->deletions.contains(idx)) - idx = npos; + if (changes->deletions.contains(idx)) { + // check if this deletion was actually a move + auto it = lower_bound(begin(moves), end(moves), idx, + [](auto const& a, auto b) { return a.from < b; }); + idx = it != moves.end() && it->from == idx ? it->to : npos; + } else - REALM_ASSERT_DEBUG(!changes->insertions.contains(idx)); + idx = changes->insertions.shift(changes->deletions.unshift(idx)); } - if (m_target_is_in_table_order && !m_sort) + if (m_target_is_in_table_order && !m_descriptor_ordering.will_apply_sort()) move_candidates = changes->insertions; } @@ -144,17 +161,20 @@ void ResultsNotifier::calculate_changes() void ResultsNotifier::run() { + // Table's been deleted, so report all rows as deleted + if (!m_query->get_table()->is_attached()) { + m_changes = {}; + m_changes.deletions.set(m_previous_rows.size()); + m_previous_rows.clear(); + return; + } + if (!need_to_run()) return; m_query->sync_view_if_needed(); m_tv = m_query->find_all(); - if (m_sort) { - m_tv.sort(m_sort); - } - if (m_distinct) { - m_tv.distinct(m_distinct); - } + m_tv.apply_descriptor_ordering(m_descriptor_ordering); m_last_seen_version = m_tv.sync_if_needed(); calculate_changes(); @@ -170,7 +190,7 @@ void ResultsNotifier::do_prepare_handover(SharedGroup& sg) // add_changes() needs to be called even if there are no changes to // clear the skip flag on the callbacks - add_changes({}); + add_changes(std::move(m_changes)); return; } @@ -218,8 +238,7 @@ void ResultsNotifier::do_attach_to(SharedGroup& sg) { REALM_ASSERT(m_query_handover); m_query = sg.import_from_handover(std::move(m_query_handover)); - m_sort = SortDescriptor::create_from_and_consume_patch(m_sort_handover, *m_query->get_table()); - m_distinct = SortDescriptor::create_from_and_consume_patch(m_distinct_handover, *m_query->get_table()); + m_descriptor_ordering = DescriptorOrdering::create_from_and_consume_patch(m_ordering_handover, *m_query->get_table()); } void ResultsNotifier::do_detach_from(SharedGroup& sg) @@ -227,8 +246,7 @@ void ResultsNotifier::do_detach_from(SharedGroup& sg) REALM_ASSERT(m_query); REALM_ASSERT(!m_tv.is_attached()); - SortDescriptor::generate_patch(m_sort, m_sort_handover); - SortDescriptor::generate_patch(m_distinct, m_distinct_handover); + DescriptorOrdering::generate_patch(m_descriptor_ordering, m_ordering_handover); m_query_handover = sg.export_for_handover(*m_query, MutableSourcePayload::Move); m_query = nullptr; } diff --git a/Pods/Realm/Realm/ObjectStore/src/impl/transact_log_handler.cpp b/Pods/Realm/Realm/ObjectStore/src/impl/transact_log_handler.cpp index 1e8a476..2b3ff27 100644 --- a/Pods/Realm/Realm/ObjectStore/src/impl/transact_log_handler.cpp +++ b/Pods/Realm/Realm/ObjectStore/src/impl/transact_log_handler.cpp @@ -47,11 +47,14 @@ class KVOAdapter : public _impl::TransactionChangeInfo { struct ListInfo { BindingContext::ObserverState* observer; - size_t col; _impl::CollectionChangeBuilder builder; + size_t col; + size_t initial_size; }; std::vector m_lists; VersionID m_version; + + size_t new_table_ndx(size_t ndx) const { return ndx < table_indices.size() ? table_indices[ndx] : ndx; } }; KVOAdapter::KVOAdapter(std::vector& observers, BindingContext* context) @@ -74,9 +77,11 @@ KVOAdapter::KVOAdapter(std::vector& observers, Bi for (auto& observer : observers) { auto table = group.get_table(observer.table_ndx); for (size_t i = 0, count = table->get_column_count(); i < count; ++i) { - if (table->get_column_type(i) != type_LinkList) - continue; - m_lists.push_back({&observer, i, {}}); + auto type = table->get_column_type(i); + if (type == type_LinkList) + m_lists.push_back({&observer, {}, i, size_t(-1)}); + else if (type == type_Table) + m_lists.push_back({&observer, {}, i, table->get_subtable_size(i, observer.row_ndx)}); } } @@ -103,9 +108,7 @@ void KVOAdapter::before(SharedGroup& sg) return; for (auto& observer : m_observers) { - size_t table_ndx = observer.table_ndx; - if (table_ndx < table_indices.size()) - table_ndx = table_indices[table_ndx]; + size_t table_ndx = new_table_ndx(observer.table_ndx); if (table_ndx >= tables.size()) continue; @@ -141,24 +144,58 @@ void KVOAdapter::before(SharedGroup& sg) } for (auto& list : m_lists) { - if (list.builder.empty()) + if (list.builder.empty()) { + // We may have pre-emptively marked the column as modified if the + // LinkList was selected but the actual changes made ended up being + // a no-op + if (list.col < list.observer->changes.size()) + list.observer->changes[list.col].kind = BindingContext::ColumnInfo::Kind::None; + continue; + } + // If the containing row was deleted then changes will be empty + if (list.observer->changes.empty()) { + REALM_ASSERT_DEBUG(tables[new_table_ndx(list.observer->table_ndx)].deletions.contains(list.observer->row_ndx)); continue; - // column should have been marked as modified and thus already exist in `changes` + } + // otherwise the column should have been marked as modified REALM_ASSERT(list.col < list.observer->changes.size()); auto& builder = list.builder; auto& changes = list.observer->changes[list.col]; + builder.modifications.remove(builder.insertions); + // KVO can't express moves (becaue NSArray doesn't have them), so // transform them into a series of sets on each affected index when possible - if (!builder.insertions.empty() && builder.insertions.count() == builder.moves.size()) { + if (!builder.moves.empty() && builder.insertions.count() == builder.moves.size() && builder.deletions.count() == builder.moves.size()) { changes.kind = BindingContext::ColumnInfo::Kind::Set; changes.indices = builder.modifications; - - size_t start = std::min(builder.insertions.begin()->first, builder.deletions.begin()->first); - size_t end = std::max(std::prev(builder.insertions.end())->second, - std::prev(builder.deletions.end())->second); - for (size_t i = start; i < end; ++i) - changes.indices.add(i); + changes.indices.add(builder.deletions); + + // Iterate over each of the rows which may have been shifted by + // the moves and check if it actually has been, or if it's ended + // up in the same place as it started (either because the moves were + // actually a swap that doesn't effect the rows in between, or the + // combination of moves happen to leave some intermediate rows in + // the same place) + auto in_range = [](auto& it, auto end, size_t i) { + if (it != end && i >= it->second) + ++it; + return it != end && i >= it->first && i < it->second; + }; + + auto del_it = builder.deletions.begin(), del_end = builder.deletions.end(); + auto ins_it = builder.insertions.begin(), ins_end = builder.insertions.end(); + size_t start = std::min(ins_it->first, del_it->first); + size_t end = std::max(std::prev(ins_end)->second, std::prev(del_end)->second); + ptrdiff_t shift = 0; + for (size_t i = start; i < end; ++i) { + if (in_range(del_it, del_end, i)) + --shift; + else if (in_range(ins_it, ins_end, i + shift)) + ++shift; + if (shift != 0) + changes.indices.add(i); + } } // KVO can't express multiple types of changes at once else if (builder.insertions.empty() + builder.modifications.empty() + builder.deletions.empty() < 2) { @@ -175,7 +212,12 @@ void KVOAdapter::before(SharedGroup& sg) else { REALM_ASSERT(!builder.deletions.empty()); changes.kind = BindingContext::ColumnInfo::Kind::Remove; - changes.indices = builder.deletions; + // Table clears don't come with the size, so we need to fix up the + // notification to make it just delete all rows that actually existed + if (std::prev(builder.deletions.end())->second > list.initial_size) + changes.indices.set(list.initial_size); + else + changes.indices = builder.deletions; } } m_context->will_change(m_observers, m_invalidated); @@ -221,6 +263,8 @@ struct MarkDirtyMixin { bool set_int_unique(size_t, size_t, size_t, int_fast64_t) { return true; } bool set_string_unique(size_t, size_t, size_t, StringData) { return true; } + + bool add_row_with_key(size_t, size_t, size_t, int64_t) { return true; } }; class TransactLogValidationMixin { @@ -231,18 +275,13 @@ class TransactLogValidationMixin { REALM_NOINLINE void schema_error() { - throw std::logic_error("Schema mismatch detected: another process has modified the Realm file's schema in an incompatible way"); + throw _impl::UnsupportedSchemaChange(); } protected: size_t current_table() const noexcept { return m_current_table; } public: - bool select_descriptor(int levels, const size_t*) - { - // subtables not supported - return levels == 0; - } bool select_table(size_t group_level_ndx, int, const size_t*) noexcept { @@ -272,11 +311,13 @@ class TransactLogValidationMixin { // Non-schema changes are all allowed void parse_complete() { } + bool select_descriptor(int, const size_t*) { return true; } bool select_link_list(size_t, size_t, size_t) { return true; } bool insert_empty_rows(size_t, size_t, size_t, bool) { return true; } bool erase_rows(size_t, size_t, size_t, bool) { return true; } bool swap_rows(size_t, size_t) { return true; } - bool clear_table() noexcept { return true; } + bool move_row(size_t, size_t) { return true; } + bool clear_table(size_t=0) noexcept { return true; } bool link_list_set(size_t, size_t, size_t) { return true; } bool link_list_insert(size_t, size_t, size_t) { return true; } bool link_list_erase(size_t, size_t) { return true; } @@ -342,9 +383,8 @@ void expand_to(std::vector& cols, size_t i) if (old_size > i) return; - auto new_size = std::max(old_size * 2, i + 1); - cols.resize(new_size); - std::iota(&cols[old_size], &cols[new_size], old_size == 0 ? 0 : cols[old_size - 1] + 1); + cols.resize(std::max(old_size * 2, i + 1)); + std::iota(begin(cols) + old_size, end(cols), old_size == 0 ? 0 : cols[old_size - 1] + 1); } void adjust_ge(std::vector& values, size_t i) @@ -358,81 +398,116 @@ void adjust_ge(std::vector& values, size_t i) // Extends TransactLogValidator to track changes made to LinkViews class TransactLogObserver : public TransactLogValidationMixin, public MarkDirtyMixin { _impl::TransactionChangeInfo& m_info; - _impl::CollectionChangeBuilder* m_active = nullptr; + _impl::CollectionChangeBuilder* m_active_list = nullptr; + _impl::CollectionChangeBuilder* m_active_table = nullptr; + _impl::CollectionChangeBuilder* m_active_descriptor = nullptr; - _impl::CollectionChangeBuilder* get_change() - { - auto tbl_ndx = current_table(); - if (!m_info.track_all && (tbl_ndx >= m_info.table_modifications_needed.size() || !m_info.table_modifications_needed[tbl_ndx])) - return nullptr; - if (m_info.tables.size() <= tbl_ndx) { - m_info.tables.resize(std::max(m_info.tables.size() * 2, tbl_ndx + 1)); - } - return &m_info.tables[tbl_ndx]; - } + bool m_need_move_info = false; + bool m_is_top_level_table = true; - bool need_move_info() const + _impl::CollectionChangeBuilder* find_list(size_t tbl, size_t col, size_t row) { - auto tbl_ndx = current_table(); - return m_info.track_all || (tbl_ndx < m_info.table_moves_needed.size() && m_info.table_moves_needed[tbl_ndx]); + // When there are multiple source versions there could be multiple + // change objects for a single LinkView, in which case we need to use + // the last one + for (auto it = m_info.lists.rbegin(), end = m_info.lists.rend(); it != end; ++it) { + if (it->table_ndx == tbl && it->row_ndx == row && it->col_ndx == col) + return it->changes; + } + return nullptr; } - public: TransactLogObserver(_impl::TransactionChangeInfo& info) : m_info(info) { } void mark_dirty(size_t row, size_t col) { - if (auto change = get_change()) - change->modify(row, col); + if (m_active_table) + m_active_table->modify(row, col); } void parse_complete() { - for (auto& table : m_info.tables) { + for (auto& table : m_info.tables) table.parse_complete(); - } - for (auto& list : m_info.lists) { + for (auto& list : m_info.lists) list.changes->clean_up_stale_moves(); - } } - bool select_link_list(size_t col, size_t row, size_t) + bool select_descriptor(int levels, const size_t*) noexcept { - mark_dirty(row, col); + if (levels == 0) // schema of selected table is being modified + m_active_descriptor = m_active_table; + else // schema of subtable is being modified; currently don't need to track this + m_active_descriptor = nullptr; + return true; + } - m_active = nullptr; - // When there are multiple source versions there could be multiple - // change objects for a single LinkView, in which case we need to use - // the last one - for (auto it = m_info.lists.rbegin(), end = m_info.lists.rend(); it != end; ++it) { - if (it->table_ndx == current_table() && it->row_ndx == row && it->col_ndx == col) { - m_active = it->changes; - break; + bool select_table(size_t group_level_ndx, int len, size_t const* path) noexcept + { + TransactLogValidationMixin::select_table(group_level_ndx, len, path); + m_active_table = nullptr; + m_is_top_level_table = true; + + // Nested subtables currently not supported + if (len > 1) { + m_is_top_level_table = false; + return true; + } + + auto tbl_ndx = current_table(); + if (!m_info.track_all && (tbl_ndx >= m_info.table_modifications_needed.size() || !m_info.table_modifications_needed[tbl_ndx])) + return true; + + m_need_move_info = m_info.track_all || (tbl_ndx < m_info.table_moves_needed.size() && + m_info.table_moves_needed[tbl_ndx]); + if (m_info.tables.size() <= tbl_ndx) + m_info.tables.resize(std::max(m_info.tables.size() * 2, tbl_ndx + 1)); + m_active_table = &m_info.tables[tbl_ndx]; + + if (len == 1) { + // Mark the cell containing the subtable as modified since selecting + // a table is always followed by a modification of some sort + size_t col = path[0]; + size_t row = path[1]; + mark_dirty(row, col); + + m_active_table = nullptr; + m_is_top_level_table = false; + if (auto table = find_list(current_table(), col, row)) { + m_active_table = table; + m_need_move_info = true; } } return true; } + bool select_link_list(size_t col, size_t row, size_t) + { + mark_dirty(row, col); + m_active_list = find_list(current_table(), col, row); + return true; + } + bool link_list_set(size_t index, size_t, size_t) { - if (m_active) - m_active->modify(index); + if (m_active_list) + m_active_list->modify(index); return true; } bool link_list_insert(size_t index, size_t, size_t) { - if (m_active) - m_active->insert(index); + if (m_active_list) + m_active_list->insert(index); return true; } bool link_list_erase(size_t index, size_t) { - if (m_active) - m_active->erase(index); + if (m_active_list) + m_active_list->erase(index); return true; } @@ -450,22 +525,24 @@ class TransactLogObserver : public TransactLogValidationMixin, public MarkDirtyM bool link_list_clear(size_t old_size) { - if (m_active) - m_active->clear(old_size); + if (m_active_list) + m_active_list->clear(old_size); return true; } bool link_list_move(size_t from, size_t to) { - if (m_active) - m_active->move(from, to); + if (m_active_list) + m_active_list->move(from, to); return true; } bool insert_empty_rows(size_t row_ndx, size_t num_rows_to_insert, size_t, bool) { - if (auto change = get_change()) - change->insert(row_ndx, num_rows_to_insert, need_move_info()); + if (m_active_table) + m_active_table->insert(row_ndx, num_rows_to_insert, m_need_move_info); + if (!m_is_top_level_table) + return true; for (auto& list : m_info.lists) { if (list.table_ndx == current_table() && list.row_ndx >= row_ndx) list.row_ndx += num_rows_to_insert; @@ -473,36 +550,55 @@ class TransactLogObserver : public TransactLogValidationMixin, public MarkDirtyM return true; } + bool add_row_with_key(size_t row_ndx, size_t prior_num_rows, size_t, int64_t) + { + insert_empty_rows(row_ndx, 1, prior_num_rows, false); + return true; + } + bool erase_rows(size_t row_ndx, size_t, size_t prior_num_rows, bool unordered) { if (!unordered) { - if (auto change = get_change()) - change->deletions.add(row_ndx); + if (m_active_table) + m_active_table->erase(row_ndx); return true; } - REALM_ASSERT(unordered); size_t last_row = prior_num_rows - 1; + if (m_active_table) + m_active_table->move_over(row_ndx, last_row, m_need_move_info); - for (auto it = begin(m_info.lists); it != end(m_info.lists); ) { - if (it->table_ndx == current_table()) { - if (it->row_ndx == row_ndx) { - *it = std::move(m_info.lists.back()); - m_info.lists.pop_back(); - continue; - } - if (it->row_ndx == last_row) - it->row_ndx = row_ndx; + if (!m_is_top_level_table) + return true; + for (size_t i = 0; i < m_info.lists.size(); ++i) { + auto& list = m_info.lists[i]; + if (list.table_ndx != current_table()) + continue; + if (list.row_ndx == row_ndx) { + if (i + 1 < m_info.lists.size()) + m_info.lists[i] = std::move(m_info.lists.back()); + m_info.lists.pop_back(); + continue; } - ++it; + if (list.row_ndx == last_row) + list.row_ndx = row_ndx; } - if (auto change = get_change()) - change->move_over(row_ndx, last_row, need_move_info()); return true; } bool swap_rows(size_t row_ndx_1, size_t row_ndx_2) { REALM_ASSERT(row_ndx_1 < row_ndx_2); + if (!m_is_top_level_table) { + if (m_active_table) { + m_active_table->move(row_ndx_1, row_ndx_2); + if (row_ndx_1 + 1 != row_ndx_2) + m_active_table->move(row_ndx_2 - 1, row_ndx_1); + } + return true; + } + + if (m_active_table) + m_active_table->swap(row_ndx_1, row_ndx_2, m_need_move_info); for (auto& list : m_info.lists) { if (list.table_ndx == current_table()) { if (list.row_ndx == row_ndx_1) @@ -511,37 +607,52 @@ class TransactLogObserver : public TransactLogValidationMixin, public MarkDirtyM list.row_ndx = row_ndx_1; } } - if (auto change = get_change()) - change->swap(row_ndx_1, row_ndx_2, need_move_info()); + return true; + } + + bool move_row(size_t from_ndx, size_t to_ndx) { + // Move row is not supported for top level tables: + REALM_ASSERT(!m_active_table || !m_is_top_level_table); + + if (m_active_table) + m_active_table->move(from_ndx, to_ndx); return true; } bool merge_rows(size_t from, size_t to) { + if (m_active_table) + m_active_table->subsume(from, to, m_need_move_info); + if (!m_is_top_level_table) + return true; for (auto& list : m_info.lists) { if (list.table_ndx == current_table() && list.row_ndx == from) list.row_ndx = to; } - if (auto change = get_change()) - change->subsume(from, to, need_move_info()); return true; } - bool clear_table() + bool clear_table(size_t=0) { auto tbl_ndx = current_table(); + if (m_active_table) + m_active_table->clear(std::numeric_limits::max()); + if (!m_is_top_level_table) + return true; auto it = remove_if(begin(m_info.lists), end(m_info.lists), [&](auto const& lv) { return lv.table_ndx == tbl_ndx; }); m_info.lists.erase(it, end(m_info.lists)); - if (auto change = get_change()) - change->clear(std::numeric_limits::max()); return true; } bool insert_column(size_t ndx, DataType, StringData, bool) { - if (auto change = get_change()) - change->insert_column(ndx); + m_info.schema_changed = true; + + if (m_active_descriptor) + m_active_descriptor->insert_column(ndx); + if (m_active_descriptor != m_active_table || !m_is_top_level_table) + return true; for (auto& list : m_info.lists) { if (list.table_ndx == current_table() && list.col_ndx >= ndx) ++list.col_ndx; @@ -565,6 +676,8 @@ class TransactLogObserver : public TransactLogValidationMixin, public MarkDirtyM bool insert_group_level_table(size_t ndx, size_t, StringData) { + m_info.schema_changed = true; + for (auto& list : m_info.lists) { if (list.table_ndx >= ndx) ++list.table_ndx; @@ -579,8 +692,12 @@ class TransactLogObserver : public TransactLogValidationMixin, public MarkDirtyM bool move_column(size_t from, size_t to) { - if (auto change = get_change()) - change->move_column(from, to); + m_info.schema_changed = true; + + if (m_active_descriptor) + m_active_descriptor->move_column(from, to); + if (m_active_descriptor != m_active_table || !m_is_top_level_table) + return true; for (auto& list : m_info.lists) { if (list.table_ndx == current_table()) adjust_for_move(list.col_ndx, from, to); @@ -594,6 +711,8 @@ class TransactLogObserver : public TransactLogValidationMixin, public MarkDirtyM bool move_group_level_table(size_t from, size_t to) { + m_info.schema_changed = true; + for (auto& list : m_info.lists) adjust_for_move(list.table_ndx, from, to); @@ -615,9 +734,9 @@ class KVOTransactLogObserver : public TransactLogObserver { public: KVOTransactLogObserver(std::vector& observers, - BindingContext* context, - _impl::NotifierPackage& notifiers, - SharedGroup& sg) + BindingContext* context, + _impl::NotifierPackage& notifiers, + SharedGroup& sg) : TransactLogObserver(m_adapter) , m_adapter(observers, context) , m_notifiers(notifiers) @@ -642,10 +761,10 @@ class KVOTransactLogObserver : public TransactLogObserver { }; template -void advance_with_notifications(BindingContext* context, SharedGroup& sg, Func&& func, - _impl::NotifierPackage& notifiers) +void advance_with_notifications(BindingContext* context, const std::unique_ptr& sg, + Func&& func, _impl::NotifierPackage& notifiers) { - auto old_version = sg.get_version_of_current_transaction(); + auto old_version = sg->get_version_of_current_transaction(); std::vector observers; if (context) { observers = context->get_observed_rows(); @@ -657,21 +776,33 @@ void advance_with_notifications(BindingContext* context, SharedGroup& sg, Func&& if (observers.empty() && (!notifiers || notifiers.version())) { notifiers.before_advance(); func(TransactLogValidator()); - auto new_version = sg.get_version_of_current_transaction(); + auto new_version = sg->get_version_of_current_transaction(); if (context && old_version != new_version) context->did_change({}, {}); + if (!sg) // did_change() could close the Realm. Just return if it does. + return; + if (context) + context->will_send_notifications(); + if (!sg) // will_send_notifications() could close the Realm. Just return if it does. + return; // did_change() can change the read version, and if it does we can't // deliver notifiers - if (new_version == sg.get_version_of_current_transaction()) - notifiers.deliver(sg); + if (new_version == sg->get_version_of_current_transaction()) + notifiers.deliver(*sg); notifiers.after_advance(); + if (sg && context) + context->did_send_notifications(); return; } - func(KVOTransactLogObserver(observers, context, notifiers, sg)); - notifiers.package_and_wait(sg.get_version_of_current_transaction().version); // is a no-op if parse_complete() was called - notifiers.deliver(sg); + if (context) + context->will_send_notifications(); + func(KVOTransactLogObserver(observers, context, notifiers, *sg)); + notifiers.package_and_wait(sg->get_version_of_current_transaction().version); // is a no-op if parse_complete() was called + notifiers.deliver(*sg); notifiers.after_advance(); + if (context) + context->did_send_notifications(); } } // anonymous namespace @@ -679,16 +810,21 @@ void advance_with_notifications(BindingContext* context, SharedGroup& sg, Func&& namespace realm { namespace _impl { +UnsupportedSchemaChange::UnsupportedSchemaChange() +: std::logic_error("Schema mismatch detected: another process has modified the Realm file's schema in an incompatible way") +{ +} + namespace transaction { void advance(SharedGroup& sg, BindingContext*, VersionID version) { LangBindHelper::advance_read(sg, TransactLogValidator(), version); } -void advance(SharedGroup& sg, BindingContext* context, NotifierPackage& notifiers) +void advance(const std::unique_ptr& sg, BindingContext* context, NotifierPackage& notifiers) { advance_with_notifications(context, sg, [&](auto&&... args) { - LangBindHelper::advance_read(sg, std::move(args)..., notifiers.version().value_or(VersionID{})); + LangBindHelper::advance_read(*sg, std::move(args)..., notifiers.version().value_or(VersionID{})); }, notifiers); } @@ -697,10 +833,10 @@ void begin_without_validation(SharedGroup& sg) LangBindHelper::promote_to_write(sg); } -void begin(SharedGroup& sg, BindingContext* context, NotifierPackage& notifiers) +void begin(const std::unique_ptr& sg, BindingContext* context, NotifierPackage& notifiers) { advance_with_notifications(context, sg, [&](auto&&... args) { - LangBindHelper::promote_to_write(sg, std::move(args)...); + LangBindHelper::promote_to_write(*sg, std::move(args)...); }, notifiers); } @@ -732,7 +868,6 @@ void advance(SharedGroup& sg, TransactionChangeInfo& info, VersionID version) else { LangBindHelper::advance_read(sg, TransactLogObserver(info), version); } - } } // namespace transaction diff --git a/Pods/Realm/Realm/ObjectStore/src/index_set.cpp b/Pods/Realm/Realm/ObjectStore/src/index_set.cpp index a5c30c2..e1a3b10 100644 --- a/Pods/Realm/Realm/ObjectStore/src/index_set.cpp +++ b/Pods/Realm/Realm/ObjectStore/src/index_set.cpp @@ -80,7 +80,7 @@ void ChunkedRangeVector::push_back(value_type value) range.end = value.second; } else { - m_data.push_back({{std::move(value)}, value.first, value.second, value.second - value.first}); + m_data.push_back({{value}, value.first, value.second, value.second - value.first}); } verify(); } @@ -141,7 +141,7 @@ ChunkedRangeVector::iterator ChunkedRangeVector::ensure_space(iterator pos) return pos; } -ChunkedRangeVector::iterator ChunkedRangeVector::erase(iterator pos) +ChunkedRangeVector::iterator ChunkedRangeVector::erase(iterator pos) noexcept { auto offset = pos.offset(); auto& chunk = *pos.m_outer; @@ -270,13 +270,13 @@ IndexSet::IndexSet(std::initializer_list values) add(v); } -bool IndexSet::contains(size_t index) const +bool IndexSet::contains(size_t index) const noexcept { auto it = const_cast(this)->find(index); return it != end() && it->first <= index; } -size_t IndexSet::count(size_t start_index, size_t end_index) const +size_t IndexSet::count(size_t start_index, size_t end_index) const noexcept { auto it = const_cast(this)->find(start_index); const auto end = this->end(); @@ -320,12 +320,12 @@ size_t IndexSet::count(size_t start_index, size_t end_index) const return ret; } -IndexSet::iterator IndexSet::find(size_t index) +IndexSet::iterator IndexSet::find(size_t index) noexcept { return find(index, begin()); } -IndexSet::iterator IndexSet::find(size_t index, iterator begin) +IndexSet::iterator IndexSet::find(size_t index, iterator begin) noexcept { auto it = std::find_if(begin.outer(), m_data.end(), [&](auto const& lft) { return lft.end > index; }); @@ -653,7 +653,7 @@ void IndexSet::remove(realm::IndexSet const& values) } } -size_t IndexSet::shift(size_t index) const +size_t IndexSet::shift(size_t index) const noexcept { // FIXME: optimize for (auto range : *this) { @@ -664,13 +664,13 @@ size_t IndexSet::shift(size_t index) const return index; } -size_t IndexSet::unshift(size_t index) const +size_t IndexSet::unshift(size_t index) const noexcept { REALM_ASSERT_DEBUG(!contains(index)); return index - count(0, index); } -void IndexSet::clear() +void IndexSet::clear() noexcept { m_data.clear(); } diff --git a/Pods/Realm/Realm/ObjectStore/src/list.cpp b/Pods/Realm/Realm/ObjectStore/src/list.cpp index c1d3591..12b4082 100644 --- a/Pods/Realm/Realm/ObjectStore/src/list.cpp +++ b/Pods/Realm/Realm/ObjectStore/src/list.cpp @@ -19,16 +19,17 @@ #include "list.hpp" #include "impl/list_notifier.hpp" +#include "impl/primitive_list_notifier.hpp" #include "impl/realm_coordinator.hpp" +#include "object_schema.hpp" #include "object_store.hpp" #include "results.hpp" #include "schema.hpp" #include "shared_realm.hpp" -#include "util/format.hpp" #include -using namespace realm; +namespace realm { using namespace realm::_impl; List::List() noexcept = default; @@ -39,18 +40,46 @@ List& List::operator=(const List&) = default; List::List(List&&) = default; List& List::operator=(List&&) = default; +List::List(std::shared_ptr r, Table& parent_table, size_t col, size_t row) +: m_realm(std::move(r)) +{ + auto type = parent_table.get_column_type(col); + REALM_ASSERT(type == type_LinkList || type == type_Table); + if (type == type_LinkList) { + m_link_view = parent_table.get_linklist(col, row); + m_table.reset(&m_link_view->get_target_table()); + } + else { + m_table = parent_table.get_subtable(col, row); + } +} + List::List(std::shared_ptr r, LinkViewRef l) noexcept : m_realm(std::move(r)) , m_link_view(std::move(l)) { + m_table.reset(&m_link_view->get_target_table()); +} + +List::List(std::shared_ptr r, TableRef t) noexcept +: m_realm(std::move(r)) +, m_table(std::move(t)) +{ +} + +static StringData object_name(Table const& table) +{ + return ObjectStore::object_type_for_table_name(table.get_name()); } -const ObjectSchema& List::get_object_schema() const +ObjectSchema const& List::get_object_schema() const { verify_attached(); + REALM_ASSERT(m_link_view); if (!m_object_schema) { - auto object_type = ObjectStore::object_type_for_table_name(m_link_view->get_target_table().get_name()); + REALM_ASSERT(get_type() == PropertyType::Object); + auto object_type = object_name(m_link_view->get_target_table()); auto it = m_realm->schema().find(object_type); REALM_ASSERT(it != m_realm->schema().end()); m_object_schema = &*it; @@ -61,27 +90,41 @@ const ObjectSchema& List::get_object_schema() const Query List::get_query() const { verify_attached(); - return m_link_view->get_target_table().where(m_link_view); + return m_link_view ? m_table->where(m_link_view) : m_table->where(); } size_t List::get_origin_row_index() const { verify_attached(); - return m_link_view->get_origin_row_index(); + return m_link_view ? m_link_view->get_origin_row_index() : m_table->get_parent_row_index(); } void List::verify_valid_row(size_t row_ndx, bool insertion) const { - size_t size = m_link_view->size(); - if (row_ndx > size || (!insertion && row_ndx == size)) { - throw OutOfBoundsIndexException{row_ndx, size + insertion}; + size_t s = size(); + if (row_ndx > s || (!insertion && row_ndx == s)) { + throw OutOfBoundsIndexException{row_ndx, s + insertion}; } } +void List::validate(RowExpr row) const +{ + if (!row.is_attached()) + throw std::invalid_argument("Object has been deleted or invalidated"); + if (row.get_table() != &m_link_view->get_target_table()) + throw std::invalid_argument(util::format("Object of type (%1) does not match List type (%2)", + object_name(*row.get_table()), + object_name(m_link_view->get_target_table()))); +} + bool List::is_valid() const { + if (!m_realm) + return false; m_realm->verify_thread(); - return m_link_view && m_link_view->is_attached(); + if (m_link_view) + return m_link_view->is_attached(); + return m_table && m_table->is_attached(); } void List::verify_attached() const @@ -94,46 +137,119 @@ void List::verify_attached() const void List::verify_in_transaction() const { verify_attached(); - if (!m_realm->is_in_transaction()) { - throw InvalidTransactionException("Must be in a write transaction"); - } + m_realm->verify_in_write(); } size_t List::size() const { verify_attached(); - return m_link_view->size(); + return m_link_view ? m_link_view->size() : m_table->size(); +} + +size_t List::to_table_ndx(size_t row) const noexcept +{ + return m_link_view ? m_link_view->get(row).get_index() : row; } -RowExpr List::get(size_t row_ndx) const +PropertyType List::get_type() const { verify_attached(); - verify_valid_row(row_ndx); - return m_link_view->get(row_ndx); + return m_link_view ? PropertyType::Object + : ObjectSchema::from_core_type(*m_table->get_descriptor(), 0); +} + +namespace { +template +auto get(Table& table, size_t row) +{ + return table.get(0, row); } -size_t List::get_unchecked(size_t row_ndx) const noexcept +template<> +auto get(Table& table, size_t row) { - return m_link_view->get(row_ndx).get_index(); + return table.get(row); +} } -size_t List::find(ConstRow const& row) const +template +T List::get(size_t row_ndx) const +{ + verify_valid_row(row_ndx); + return realm::get(*m_table, to_table_ndx(row_ndx)); +} + +template RowExpr List::get(size_t) const; + +template +size_t List::find(T const& value) const { verify_attached(); + return m_table->find_first(0, value); +} - if (!row.is_attached() || row.get_table() != &m_link_view->get_target_table()) { +template<> +size_t List::find(RowExpr const& row) const +{ + verify_attached(); + if (!row.is_attached()) return not_found; + validate(row); + + return m_link_view ? m_link_view->find(row.get_index()) : row.get_index(); +} + +size_t List::find(Query&& q) const +{ + verify_attached(); + if (m_link_view) { + size_t index = get_query().and_query(std::move(q)).find(); + return index == not_found ? index : m_link_view->find(index); } + return q.find(); +} - return m_link_view->find(row.get_index()); +template +void List::add(T value) +{ + verify_in_transaction(); + m_table->set(0, m_table->add_empty_row(), value); } +template<> void List::add(size_t target_row_ndx) { verify_in_transaction(); m_link_view->add(target_row_ndx); } +template<> +void List::add(RowExpr row) +{ + validate(row); + add(row.get_index()); +} + +template<> +void List::add(int value) +{ + verify_in_transaction(); + if (m_link_view) + add(static_cast(value)); + else + add(static_cast(value)); +} + +template +void List::insert(size_t row_ndx, T value) +{ + verify_in_transaction(); + verify_valid_row(row_ndx, true); + m_table->insert_empty_row(row_ndx); + m_table->set(0, row_ndx, value); +} + +template<> void List::insert(size_t row_ndx, size_t target_row_ndx) { verify_in_transaction(); @@ -141,27 +257,55 @@ void List::insert(size_t row_ndx, size_t target_row_ndx) m_link_view->insert(row_ndx, target_row_ndx); } +template<> +void List::insert(size_t row_ndx, RowExpr row) +{ + validate(row); + insert(row_ndx, row.get_index()); +} + void List::move(size_t source_ndx, size_t dest_ndx) { verify_in_transaction(); verify_valid_row(source_ndx); verify_valid_row(dest_ndx); // Can't be one past end due to removing one earlier - m_link_view->move(source_ndx, dest_ndx); + if (source_ndx == dest_ndx) + return; + + if (m_link_view) + m_link_view->move(source_ndx, dest_ndx); + else + m_table->move_row(source_ndx, dest_ndx); } void List::remove(size_t row_ndx) { verify_in_transaction(); verify_valid_row(row_ndx); - m_link_view->remove(row_ndx); + if (m_link_view) + m_link_view->remove(row_ndx); + else + m_table->remove(row_ndx); } void List::remove_all() { verify_in_transaction(); - m_link_view->clear(); + if (m_link_view) + m_link_view->clear(); + else + m_table->clear(); +} + +template +void List::set(size_t row_ndx, T value) +{ + verify_in_transaction(); + verify_valid_row(row_ndx); + m_table->set(0, row_ndx, value); } +template<> void List::set(size_t row_ndx, size_t target_row_ndx) { verify_in_transaction(); @@ -169,61 +313,156 @@ void List::set(size_t row_ndx, size_t target_row_ndx) m_link_view->set(row_ndx, target_row_ndx); } +template<> +void List::set(size_t row_ndx, RowExpr row) +{ + validate(row); + set(row_ndx, row.get_index()); +} + void List::swap(size_t ndx1, size_t ndx2) { verify_in_transaction(); verify_valid_row(ndx1); verify_valid_row(ndx2); - m_link_view->swap(ndx1, ndx2); + if (m_link_view) + m_link_view->swap(ndx1, ndx2); + else + m_table->swap_rows(ndx1, ndx2); +} + +void List::delete_at(size_t row_ndx) +{ + verify_in_transaction(); + verify_valid_row(row_ndx); + if (m_link_view) + m_link_view->remove_target_row(row_ndx); + else + m_table->remove(row_ndx); } void List::delete_all() { verify_in_transaction(); - m_link_view->remove_all_target_rows(); + if (m_link_view) + m_link_view->remove_all_target_rows(); + else + m_table->clear(); } -Results List::sort(SortDescriptor order) +Results List::sort(SortDescriptor order) const { verify_attached(); - return Results(m_realm, m_link_view, util::none, std::move(order)); + if (m_link_view) + return Results(m_realm, m_link_view, util::none, std::move(order)); + + DescriptorOrdering new_order; + new_order.append_sort(std::move(order)); + return Results(m_realm, get_query(), std::move(new_order)); +} + +Results List::sort(std::vector> const& keypaths) const +{ + return as_results().sort(keypaths); } -Results List::filter(Query q) +Results List::filter(Query q) const { verify_attached(); - return Results(m_realm, m_link_view, get_query().and_query(std::move(q))); + if (m_link_view) + return Results(m_realm, m_link_view, get_query().and_query(std::move(q))); + return Results(m_realm, get_query().and_query(std::move(q))); } -Results List::snapshot() const +Results List::as_results() const { verify_attached(); - return Results(m_realm, m_link_view).snapshot(); + return m_link_view ? Results(m_realm, m_link_view) : Results(m_realm, *m_table); } -// These definitions rely on that LinkViews are interned by core -bool List::operator==(List const& rgt) const noexcept +Results List::snapshot() const { - return m_link_view.get() == rgt.m_link_view.get(); + return as_results().snapshot(); } -namespace std { -size_t hash::operator()(realm::List const& list) const +util::Optional List::max(size_t column) +{ + return as_results().max(column); +} + +util::Optional List::min(size_t column) +{ + return as_results().min(column); +} + +Mixed List::sum(size_t column) +{ + // Results::sum() returns none only for Mode::Empty Results, so we can + // safely ignore that possibility here + return *as_results().sum(column); +} + +util::Optional List::average(size_t column) { - return std::hash()(list.m_link_view.get()); + return as_results().average(column); } + +// These definitions rely on that LinkViews and Tables are interned by core +bool List::operator==(List const& rgt) const noexcept +{ + return m_link_view == rgt.m_link_view && m_table.get() == rgt.m_table.get(); } NotificationToken List::add_notification_callback(CollectionChangeCallback cb) & { verify_attached(); + // Adding a new callback to a notifier which had all of its callbacks + // removed does not properly reinitialize the notifier. Work around this by + // recreating it instead. + // FIXME: The notifier lifecycle here is dumb (when all callbacks are removed + // from a notifier a zombie is left sitting around uselessly) and should be + // cleaned up. + if (m_notifier && !m_notifier->have_callbacks()) + m_notifier.reset(); if (!m_notifier) { - m_notifier = std::make_shared(m_link_view, m_realm); + if (get_type() == PropertyType::Object) + m_notifier = std::static_pointer_cast<_impl::CollectionNotifier>(std::make_shared(m_link_view, m_realm)); + else + m_notifier = std::static_pointer_cast<_impl::CollectionNotifier>(std::make_shared(m_table, m_realm)); RealmCoordinator::register_notifier(m_notifier); } return {m_notifier, m_notifier->add_callback(std::move(cb))}; } List::OutOfBoundsIndexException::OutOfBoundsIndexException(size_t r, size_t c) -: std::out_of_range(util::format("Requested index %1 greater than max %2", r, c)) +: std::out_of_range(util::format("Requested index %1 greater than max %2", r, c - 1)) , requested(r), valid_count(c) {} + +#define REALM_PRIMITIVE_LIST_TYPE(T) \ + template T List::get(size_t) const; \ + template size_t List::find(T const&) const; \ + template void List::add(T); \ + template void List::insert(size_t, T); \ + template void List::set(size_t, T); + +REALM_PRIMITIVE_LIST_TYPE(bool) +REALM_PRIMITIVE_LIST_TYPE(int64_t) +REALM_PRIMITIVE_LIST_TYPE(float) +REALM_PRIMITIVE_LIST_TYPE(double) +REALM_PRIMITIVE_LIST_TYPE(StringData) +REALM_PRIMITIVE_LIST_TYPE(BinaryData) +REALM_PRIMITIVE_LIST_TYPE(Timestamp) +REALM_PRIMITIVE_LIST_TYPE(util::Optional) +REALM_PRIMITIVE_LIST_TYPE(util::Optional) +REALM_PRIMITIVE_LIST_TYPE(util::Optional) +REALM_PRIMITIVE_LIST_TYPE(util::Optional) + +#undef REALM_PRIMITIVE_LIST_TYPE +} // namespace realm + +namespace std { +size_t hash::operator()(realm::List const& list) const +{ + return std::hash()(list.m_link_view ? list.m_link_view.get() : (void*)list.m_table.get()); +} +} diff --git a/Pods/Realm/Realm/ObjectStore/src/object.cpp b/Pods/Realm/Realm/ObjectStore/src/object.cpp index 6d80ea1..7fd6c74 100644 --- a/Pods/Realm/Realm/ObjectStore/src/object.cpp +++ b/Pods/Realm/Realm/ObjectStore/src/object.cpp @@ -20,15 +20,44 @@ #include "impl/object_notifier.hpp" #include "impl/realm_coordinator.hpp" +#include "object_schema.hpp" +#include "object_store.hpp" using namespace realm; -Object::Object(SharedRealm r, ObjectSchema const& s, BasicRowExpr const& o) -: m_realm(std::move(r)), m_object_schema(&s), m_row(o) { } +InvalidatedObjectException::InvalidatedObjectException(const std::string& object_type) +: std::logic_error("Accessing object of type " + object_type + " which has been invalidated or deleted") +, object_type(object_type) +{} + +InvalidPropertyException::InvalidPropertyException(const std::string& object_type, const std::string& property_name) +: std::logic_error(util::format("Property '%1.%2' does not exist", object_type, property_name)) +, object_type(object_type), property_name(property_name) +{} + +MissingPropertyValueException::MissingPropertyValueException(const std::string& object_type, const std::string& property_name) +: std::logic_error(util::format("Missing value for property '%1.%2'", object_type, property_name)) +, object_type(object_type), property_name(property_name) +{} + +MissingPrimaryKeyException::MissingPrimaryKeyException(const std::string& object_type) +: std::logic_error(util::format("'%1' does not have a primary key defined", object_type)) +, object_type(object_type) +{} + +ReadOnlyPropertyException::ReadOnlyPropertyException(const std::string& object_type, const std::string& property_name) +: std::logic_error(util::format("Cannot modify read-only property '%1.%2'", object_type, property_name)) +, object_type(object_type), property_name(property_name) {} -Object::Object(SharedRealm r, ObjectSchema const& s, Row const& o) +Object::Object(SharedRealm r, ObjectSchema const& s, RowExpr const& o) : m_realm(std::move(r)), m_object_schema(&s), m_row(o) { } +Object::Object(SharedRealm r, StringData object_type, size_t ndx) +: m_realm(std::move(r)) +, m_object_schema(&*m_realm->schema().find(object_type)) +, m_row(ObjectStore::table_for_object_type(m_realm->read_group(), object_type)->get(ndx)) +{ } + Object::Object() = default; Object::~Object() = default; Object::Object(Object const&) = default; @@ -36,10 +65,52 @@ Object::Object(Object&&) = default; Object& Object::operator=(Object const&) = default; Object& Object::operator=(Object&&) = default; -NotificationToken Object::add_notification_block(CollectionChangeCallback callback) & +NotificationToken Object::add_notification_callback(CollectionChangeCallback callback) & { - if (!m_notifier) + verify_attached(); + if (!m_notifier) { m_notifier = std::make_shared<_impl::ObjectNotifier>(m_row, m_realm); - _impl::RealmCoordinator::register_notifier(m_notifier); + _impl::RealmCoordinator::register_notifier(m_notifier); + } return {m_notifier, m_notifier->add_callback(std::move(callback))}; } + +void Object::verify_attached() const +{ + m_realm->verify_thread(); + if (!m_row.is_attached()) { + throw InvalidatedObjectException(m_object_schema->name); + } +} + +Property const& Object::property_for_name(StringData prop_name) const +{ + auto prop = m_object_schema->property_for_name(prop_name); + if (!prop) { + throw InvalidPropertyException(m_object_schema->name, prop_name); + } + return *prop; +} + +#if REALM_ENABLE_SYNC +void Object::ensure_user_in_everyone_role() +{ + auto role_table = m_realm->read_group().get_table("class___Role"); + if (!role_table) + return; + size_t ndx = role_table->find_first_string(role_table->get_column_index("name"), "everyone"); + if (ndx == npos) + return; + auto users = role_table->get_linklist(role_table->get_column_index("members"), ndx); + if (users->find(m_row.get_index()) != not_found) + return; + + users->add(m_row.get_index()); +} + +void Object::ensure_private_role_exists_for_user() +{ + auto user_id = m_row.get(m_row.get_table()->get_column_index("id")); + ObjectStore::ensure_private_role_exists_for_user(m_realm->read_group(), user_id); +} +#endif diff --git a/Pods/Realm/Realm/ObjectStore/src/object_schema.cpp b/Pods/Realm/Realm/ObjectStore/src/object_schema.cpp index 269dd5b..e5082fb 100644 --- a/Pods/Realm/Realm/ObjectStore/src/object_schema.cpp +++ b/Pods/Realm/Realm/ObjectStore/src/object_schema.cpp @@ -18,46 +18,60 @@ #include "object_schema.hpp" +#include "feature_checks.hpp" #include "object_store.hpp" #include "property.hpp" #include "schema.hpp" -#include "util/format.hpp" #include +#include #include #include using namespace realm; -#define ASSERT_PROPERTY_TYPE_VALUE(property, type) \ - static_assert(static_cast(PropertyType::property) == type_##type, \ - "PropertyType and DataType must have the same values") - -ASSERT_PROPERTY_TYPE_VALUE(Int, Int); -ASSERT_PROPERTY_TYPE_VALUE(Bool, Bool); -ASSERT_PROPERTY_TYPE_VALUE(Float, Float); -ASSERT_PROPERTY_TYPE_VALUE(Double, Double); -ASSERT_PROPERTY_TYPE_VALUE(Data, Binary); -ASSERT_PROPERTY_TYPE_VALUE(Date, Timestamp); -ASSERT_PROPERTY_TYPE_VALUE(Any, Mixed); -ASSERT_PROPERTY_TYPE_VALUE(Object, Link); -ASSERT_PROPERTY_TYPE_VALUE(Array, LinkList); - ObjectSchema::ObjectSchema() = default; ObjectSchema::~ObjectSchema() = default; ObjectSchema::ObjectSchema(std::string name, std::initializer_list persisted_properties) +: ObjectSchema(std::move(name), persisted_properties, {}) +{ +} + +ObjectSchema::ObjectSchema(std::string name, std::initializer_list persisted_properties, + std::initializer_list computed_properties) : name(std::move(name)) , persisted_properties(persisted_properties) +, computed_properties(computed_properties) { for (auto const& prop : persisted_properties) { if (prop.is_primary) { primary_key = prop.name; + break; } } } +PropertyType ObjectSchema::from_core_type(Descriptor const& table, size_t col) +{ + auto optional = table.is_nullable(col) ? PropertyType::Nullable : PropertyType::Required; + switch (table.get_column_type(col)) { + case type_Int: return PropertyType::Int | optional; + case type_Float: return PropertyType::Float | optional; + case type_Double: return PropertyType::Double | optional; + case type_Bool: return PropertyType::Bool | optional; + case type_String: return PropertyType::String | optional; + case type_Binary: return PropertyType::Data | optional; + case type_Timestamp: return PropertyType::Date | optional; + case type_Mixed: return PropertyType::Any | optional; + case type_Link: return PropertyType::Object | PropertyType::Nullable; + case type_LinkList: return PropertyType::Object | PropertyType::Array; + case type_Table: return from_core_type(*table.get_subdescriptor(col), 0) | PropertyType::Array; + default: REALM_UNREACHABLE(); + } +} + ObjectSchema::ObjectSchema(Group const& group, StringData name, size_t index) : name(name) { ConstTableRef table; if (index < group.size()) { @@ -70,18 +84,9 @@ ObjectSchema::ObjectSchema(Group const& group, StringData name, size_t index) : size_t count = table->get_column_count(); persisted_properties.reserve(count); for (size_t col = 0; col < count; col++) { - Property property; - property.name = table->get_column_name(col).data(); - property.type = (PropertyType)table->get_column_type(col); - property.is_indexed = table->has_search_index(col); - property.is_nullable = table->is_nullable(col) || property.type == PropertyType::Object; - property.table_column = col; - if (property.type == PropertyType::Object || property.type == PropertyType::Array) { - // set link type for objects and arrays - ConstTableRef linkTable = table->get_link_target(col); - property.object_type = ObjectStore::object_type_for_table_name(linkTable->get_name().data()); + if (auto property = ObjectStore::property_for_column_index(table, col)) { + persisted_properties.push_back(std::move(property.value())); } - persisted_properties.push_back(std::move(property)); } primary_key = realm::ObjectStore::get_primary_key_for_object(group, name); @@ -126,30 +131,35 @@ static void validate_property(Schema const& schema, Property const** primary, std::vector& exceptions) { + if (prop.type == PropertyType::LinkingObjects && !is_array(prop.type)) { + exceptions.emplace_back("Linking Objects property '%1.%2' must be an array.", + object_name, prop.name); + } + // check nullablity - if (prop.is_nullable && !prop.type_is_nullable()) { + if (is_nullable(prop.type) && !prop.type_is_nullable()) { exceptions.emplace_back("Property '%1.%2' of type '%3' cannot be nullable.", object_name, prop.name, string_for_property_type(prop.type)); } - else if (prop.type == PropertyType::Object && !prop.is_nullable) { - exceptions.emplace_back("Property '%1.%2' of type 'Object' must be nullable.", object_name, prop.name); + else if (prop.type == PropertyType::Object && !is_nullable(prop.type) && !is_array(prop.type)) { + exceptions.emplace_back("Property '%1.%2' of type 'object' must be nullable.", object_name, prop.name); } // check primary keys if (prop.is_primary) { - if (!prop.is_indexable()) { + if (prop.type != PropertyType::Int && prop.type != PropertyType::String) { exceptions.emplace_back("Property '%1.%2' of type '%3' cannot be made the primary key.", object_name, prop.name, string_for_property_type(prop.type)); } if (*primary) { - exceptions.emplace_back("Properties'%1' and '%2' are both marked as the primary key of '%3'.", + exceptions.emplace_back("Properties '%1' and '%2' are both marked as the primary key of '%3'.", prop.name, (*primary)->name, object_name); } *primary = ∝ } // check indexable - if (prop.is_indexed && !prop.is_indexable()) { + if (prop.is_indexed && !prop.type_is_indexable()) { exceptions.emplace_back("Property '%1.%2' of type '%3' cannot be indexed.", object_name, prop.name, string_for_property_type(prop.type)); } @@ -164,10 +174,10 @@ static void validate_property(Schema const& schema, object_name, prop.name, string_for_property_type(prop.type)); } - if (prop.type != PropertyType::Object && prop.type != PropertyType::Array && prop.type != PropertyType::LinkingObjects) { + if (prop.type != PropertyType::Object && prop.type != PropertyType::LinkingObjects) { if (!prop.object_type.empty()) { exceptions.emplace_back("Property '%1.%2' of type '%3' cannot have an object type.", - object_name, prop.name, string_for_property_type(prop.type)); + object_name, prop.name, prop.type_string()); } return; } @@ -190,7 +200,7 @@ static void validate_property(Schema const& schema, prop.object_type, prop.link_origin_property_name, object_name, prop.name); } - else if (origin_property->type != PropertyType::Object && origin_property->type != PropertyType::Array) { + else if (origin_property->type != PropertyType::Object) { exceptions.emplace_back("Property '%1.%2' declared as origin of linking objects property '%3.%4' is not a link", prop.object_type, prop.link_origin_property_name, object_name, prop.name); diff --git a/Pods/Realm/Realm/ObjectStore/src/object_store.cpp b/Pods/Realm/Realm/ObjectStore/src/object_store.cpp index 8d489ec..dde618a 100644 --- a/Pods/Realm/Realm/ObjectStore/src/object_store.cpp +++ b/Pods/Realm/Realm/ObjectStore/src/object_store.cpp @@ -18,21 +18,29 @@ #include "object_store.hpp" +#include "feature_checks.hpp" #include "object_schema.hpp" #include "schema.hpp" #include "shared_realm.hpp" -#include "util/format.hpp" +#include "sync/partial_sync.hpp" +#include #include #include #include #include +#if REALM_ENABLE_SYNC +#include +#include +#include +#endif // REALM_ENABLE_SYNC + #include using namespace realm; -const uint64_t ObjectStore::NotVersioned = std::numeric_limits::max(); +constexpr uint64_t ObjectStore::NotVersioned; namespace { const char * const c_metadataTableName = "metadata"; @@ -49,31 +57,37 @@ const size_t c_zeroRowIndex = 0; const char c_object_table_prefix[] = "class_"; -void create_metadata_tables(Group& group) { - // FIXME: the order of the creation of the two tables seems to - // matter for some Android devices. The reason is unclear, and - // further investigation is required. - // See https://github.com/realm/realm-java/issues/3651 - - TableRef table = group.get_or_add_table(c_metadataTableName); - if (table->get_column_count() == 0) { - table->add_column(type_Int, c_versionColumnName); +void create_metadata_tables(Group& group, bool partial_realm) { + // The tables 'pk' and 'metadata' are treated specially by Sync. The 'pk' table + // is populated by `sync::create_table` and friends, while the 'metadata' table + // is simply ignored. + TableRef pk_table = group.get_or_add_table(c_primaryKeyTableName); + TableRef metadata_table = group.get_or_add_table(c_metadataTableName); + if (metadata_table->get_column_count() == 0) { + metadata_table->insert_column(c_versionColumnIndex, type_Int, c_versionColumnName); + metadata_table->add_empty_row(); // set initial version - table->add_empty_row(); - table->set_int(c_versionColumnIndex, c_zeroRowIndex, ObjectStore::NotVersioned); + metadata_table->set_int(c_versionColumnIndex, c_zeroRowIndex, ObjectStore::NotVersioned); } - table = group.get_or_add_table(c_primaryKeyTableName); - if (table->get_column_count() == 0) { - table->add_column(type_String, c_primaryKeyObjectClassColumnName); - table->add_column(type_String, c_primaryKeyPropertyNameColumnName); + if (pk_table->get_column_count() == 0) { + pk_table->insert_column(c_primaryKeyObjectClassColumnIndex, type_String, c_primaryKeyObjectClassColumnName); + pk_table->insert_column(c_primaryKeyPropertyNameColumnIndex, type_String, c_primaryKeyPropertyNameColumnName); } - table->add_search_index(table->get_column_index(c_primaryKeyObjectClassColumnName)); + pk_table->add_search_index(c_primaryKeyObjectClassColumnIndex); + +#if REALM_ENABLE_SYNC + // Only add __ResultSets if Realm is a partial Realm + if (partial_realm) + _impl::initialize_schema(group); +#else + (void)partial_realm; +#endif } void set_schema_version(Group& group, uint64_t version) { - TableRef table = group.get_or_add_table(c_metadataTableName); + TableRef table = group.get_table(c_metadataTableName); table->set_int(c_versionColumnIndex, c_zeroRowIndex, version); } @@ -83,16 +97,19 @@ auto table_for_object_schema(Group& group, ObjectSchema const& object_schema) return ObjectStore::table_for_object_type(group, object_schema.name); } -void add_index(Table& table, size_t col) +DataType to_core_type(PropertyType type) { - try { - table.add_search_index(col); - } - catch (LogicError const&) { - throw std::logic_error(util::format("Cannot index property '%1.%2': indexing properties of type '%3' is not yet implemented.", - ObjectStore::object_type_for_table_name(table.get_name()), - table.get_column_name(col), - string_for_property_type((PropertyType)table.get_column_type(col)))); + REALM_ASSERT(type != PropertyType::Object); // Link columns have to be handled differently + REALM_ASSERT(type != PropertyType::Any); // Mixed columns can't be created + switch (type & ~PropertyType::Flags) { + case PropertyType::Int: return type_Int; + case PropertyType::Bool: return type_Bool; + case PropertyType::Float: return type_Float; + case PropertyType::Double: return type_Double; + case PropertyType::String: return type_String; + case PropertyType::Date: return type_Timestamp; + case PropertyType::Data: return type_Binary; + default: REALM_COMPILER_HINT_UNREACHABLE(); } } @@ -102,15 +119,23 @@ void insert_column(Group& group, Table& table, Property const& property, size_t // LinkingObjects must be an artifact of an existing link column. REALM_ASSERT(property.type != PropertyType::LinkingObjects); - if (property.type == PropertyType::Object || property.type == PropertyType::Array) { + if (property.type == PropertyType::Object) { auto target_name = ObjectStore::table_name_for_object_type(property.object_type); - TableRef link_table = group.get_or_add_table(target_name); - table.insert_column_link(col_ndx, DataType(property.type), property.name, *link_table); + TableRef link_table = group.get_table(target_name); + REALM_ASSERT(link_table); + table.insert_column_link(col_ndx, is_array(property.type) ? type_LinkList : type_Link, + property.name, *link_table); + } + else if (is_array(property.type)) { + DescriptorRef desc; + table.insert_column(col_ndx, type_Table, property.name, &desc); + desc->add_column(to_core_type(property.type & ~PropertyType::Flags), ObjectStore::ArrayColumnName, + nullptr, is_nullable(property.type)); } else { - table.insert_column(col_ndx, DataType(property.type), property.name, property.is_nullable); + table.insert_column(col_ndx, to_core_type(property.type), property.name, is_nullable(property.type)); if (property.requires_index()) - add_index(table, col_ndx); + table.add_search_index(col_ndx); } } @@ -128,20 +153,39 @@ void replace_column(Group& group, Table& table, Property const& old_property, Pr TableRef create_table(Group& group, ObjectSchema const& object_schema) { auto name = ObjectStore::table_name_for_object_type(object_schema.name); - auto table = group.get_or_add_table(name); - if (table->get_column_count() > 0) { - return table; - } - for (auto const& prop : object_schema.persisted_properties) { - add_column(group, *table, prop); + TableRef table; +#if REALM_ENABLE_SYNC + if (auto* pk_property = object_schema.primary_key_property()) { + table = sync::create_table_with_primary_key(group, name, to_core_type(pk_property->type), + pk_property->name, is_nullable(pk_property->type)); } - + else { + table = sync::create_table(group, name); + } +#else + table = group.get_or_add_table(name); ObjectStore::set_primary_key_for_object(group, object_schema.name, object_schema.primary_key); +#endif // REALM_ENABLE_SYNC return table; } +void add_initial_columns(Group& group, ObjectSchema const& object_schema) +{ + auto name = ObjectStore::table_name_for_object_type(object_schema.name); + TableRef table = group.get_table(name); + + for (auto const& prop : object_schema.persisted_properties) { +#if REALM_ENABLE_SYNC + // The sync::create_table* functions create the PK column for us. + if (prop.is_primary) + continue; +#endif // REALM_ENABLE_SYNC + add_column(group, *table, prop); + } +} + void copy_property_values(Property const& prop, Table& table) { auto copy_property_values = [&](auto getter, auto setter) { @@ -152,7 +196,7 @@ void copy_property_values(Property const& prop, Table& table) } }; - switch (prop.type) { + switch (prop.type & ~PropertyType::Flags) { case PropertyType::Int: copy_property_values(&Table::get_int, &Table::set_int); break; @@ -181,7 +225,7 @@ void copy_property_values(Property const& prop, Table& table) void make_property_optional(Group& group, Table& table, Property property) { - property.is_nullable = true; + property.type |= PropertyType::Nullable; insert_column(group, table, property, property.table_column); copy_property_values(property, table); table.remove_column(property.table_column + 1); @@ -189,7 +233,7 @@ void make_property_optional(Group& group, Table& table, Property property) void make_property_required(Group& group, Table& table, Property property) { - property.is_nullable = false; + property.type &= ~PropertyType::Nullable; insert_column(group, table, property, property.table_column); table.remove_column(property.table_column + 1); } @@ -213,9 +257,8 @@ void validate_primary_column_uniqueness(Group const& group) } } // anonymous namespace -// FIXME remove this after integrating OS's migration related logic into Realm java void ObjectStore::set_schema_version(Group& group, uint64_t version) { - ::create_metadata_tables(group); + ::create_metadata_tables(group, false); ::set_schema_version(group, version); } @@ -242,13 +285,27 @@ StringData ObjectStore::get_primary_key_for_object(Group const& group, StringDat void ObjectStore::set_primary_key_for_object(Group& group, StringData object_type, StringData primary_key) { TableRef table = group.get_table(c_primaryKeyTableName); - // get row or create if new object and populate size_t row = table->find_first_string(c_primaryKeyObjectClassColumnIndex, object_type); + +#if REALM_ENABLE_SYNC + // sync::create_table* functions should have already updated the pk table. + if (sync::has_object_ids(group)) { + if (primary_key.size() == 0) + REALM_ASSERT(row == not_found); + else { + REALM_ASSERT(row != not_found); + REALM_ASSERT(table->get_string(c_primaryKeyPropertyNameColumnIndex, row) == primary_key); + } + return; + } +#endif // REALM_ENABLE_SYNC + if (row == not_found && primary_key.size()) { row = table->add_empty_row(); - row = table->set_string_unique(c_primaryKeyObjectClassColumnIndex, row, object_type); + table->set_string_unique(c_primaryKeyObjectClassColumnIndex, row, object_type); + table->set_string(c_primaryKeyPropertyNameColumnIndex, row, primary_key); + return; } - // set if changing, or remove if setting to nil if (primary_key.size() == 0) { if (row != not_found) { @@ -290,6 +347,16 @@ struct SchemaDifferenceExplainer { errors.emplace_back("Class '%1' has been added.", op.object->name); } + void operator()(schema_change::RemoveTable) + { + // We never do anything for RemoveTable + } + + void operator()(schema_change::AddInitialProperties) + { + // Nothing. Always preceded by AddTable. + } + void operator()(schema_change::AddProperty op) { errors.emplace_back("Property '%1.%2' has been added.", op.object->name, op.property->name); @@ -304,8 +371,8 @@ struct SchemaDifferenceExplainer { { errors.emplace_back("Property '%1.%2' has been changed from '%3' to '%4'.", op.object->name, op.new_property->name, - string_for_property_type(op.old_property->type), - string_for_property_type(op.new_property->type)); + op.old_property->type_string(), + op.new_property->type_string()); } void operator()(schema_change::MakePropertyNullable op) @@ -321,14 +388,14 @@ struct SchemaDifferenceExplainer { void operator()(schema_change::ChangePrimaryKey op) { if (op.property && !op.object->primary_key.empty()) { - errors.emplace_back("Primary Key for class '%1 has changed from '%2' to '%3'.", + errors.emplace_back("Primary Key for class '%1' has changed from '%2' to '%3'.", op.object->name, op.object->primary_key, op.property->name); } else if (op.property) { - errors.emplace_back("Primary Key for class '%1 has been added.", op.object->name); + errors.emplace_back("Primary Key for class '%1' has been added.", op.object->name); } else { - errors.emplace_back("Primary Key for class '%1 has been removed.", op.object->name); + errors.emplace_back("Primary Key for class '%1' has been removed.", op.object->name); } } @@ -381,8 +448,10 @@ bool ObjectStore::needs_migration(std::vector const& changes) using namespace schema_change; struct Visitor { bool operator()(AddIndex) { return false; } + bool operator()(AddInitialProperties) { return false; } bool operator()(AddProperty) { return true; } bool operator()(AddTable) { return false; } + bool operator()(RemoveTable) { return false; } bool operator()(ChangePrimaryKey) { return true; } bool operator()(ChangePropertyType) { return true; } bool operator()(MakePropertyNullable) { return true; } @@ -409,21 +478,63 @@ void ObjectStore::verify_no_migration_required(std::vector const& // Adding a table or adding/removing indexes can be done automatically. // All other changes require migrations. void operator()(AddTable) { } + void operator()(AddInitialProperties) { } void operator()(AddIndex) { } void operator()(RemoveIndex) { } } verifier; verify_no_errors(verifier, changes); } -void ObjectStore::verify_valid_additive_changes(std::vector const& changes) +bool ObjectStore::verify_valid_additive_changes(std::vector const& changes, bool update_indexes) { using namespace schema_change; struct Verifier : SchemaDifferenceExplainer { using SchemaDifferenceExplainer::operator(); + bool index_changes = false; + bool other_changes = false; + // Additive mode allows adding things, extra columns, and adding/removing indexes + void operator()(AddTable) { other_changes = true; } + void operator()(AddInitialProperties) { other_changes = true; } + void operator()(AddProperty) { other_changes = true; } + void operator()(RemoveProperty) { } + void operator()(AddIndex) { index_changes = true; } + void operator()(RemoveIndex) { index_changes = true; } + } verifier; + verify_no_errors(verifier, changes); + return verifier.other_changes || (verifier.index_changes && update_indexes); +} + +void ObjectStore::verify_valid_external_changes(std::vector const& changes) +{ + using namespace schema_change; + struct Verifier : SchemaDifferenceExplainer { + using SchemaDifferenceExplainer::operator(); + + // Adding new things is fine void operator()(AddTable) { } + void operator()(AddInitialProperties) { } void operator()(AddProperty) { } + void operator()(AddIndex) { } + void operator()(RemoveIndex) { } + + // Deleting tables is not okay + void operator()(RemoveTable op) { + errors.emplace_back("Class '%1' has been removed.", op.object->name); + } + } verifier; + verify_no_errors(verifier, changes); +} + +void ObjectStore::verify_compatible_for_immutable_and_readonly(std::vector const& changes) +{ + using namespace schema_change; + struct Verifier : SchemaDifferenceExplainer { + using SchemaDifferenceExplainer::operator(); + + void operator()(AddTable) { } + void operator()(AddInitialProperties) { } void operator()(RemoveProperty) { } void operator()(AddIndex) { } void operator()(RemoveIndex) { } @@ -444,7 +555,8 @@ static void apply_non_migration_changes(Group& group, std::vector using SchemaDifferenceExplainer::operator(); void operator()(AddTable op) { create_table(group, *op.object); } - void operator()(AddIndex op) { add_index(table(op.object), op.property->table_column); } + void operator()(AddInitialProperties op) { add_initial_columns(group, *op.object); } + void operator()(AddIndex op) { table(op.object).add_search_index(op.property->table_column); } void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->table_column); } } applier{group}; verify_no_errors(applier, changes); @@ -459,6 +571,8 @@ static void create_initial_tables(Group& group, std::vector const& TableHelper table; void operator()(AddTable op) { create_table(group, *op.object); } + void operator()(RemoveTable) { } + void operator()(AddInitialProperties op) { add_initial_columns(group, *op.object); } // Note that in normal operation none of these will be hit, as if we're // creating the initial tables there shouldn't be anything to update. @@ -470,7 +584,7 @@ static void create_initial_tables(Group& group, std::vector const& void operator()(MakePropertyNullable op) { make_property_optional(group, table(op.object), *op.property); } void operator()(MakePropertyRequired op) { make_property_required(group, table(op.object), *op.property); } void operator()(ChangePrimaryKey op) { ObjectStore::set_primary_key_for_object(group, op.object->name, op.property ? StringData{op.property->name} : ""); } - void operator()(AddIndex op) { add_index(table(op.object), op.property->table_column); } + void operator()(AddIndex op) { table(op.object).add_search_index(op.property->table_column); } void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->table_column); } void operator()(ChangePropertyType op) @@ -484,18 +598,21 @@ static void create_initial_tables(Group& group, std::vector const& } } -static void apply_additive_changes(Group& group, std::vector const& changes, bool update_indexes) +void ObjectStore::apply_additive_changes(Group& group, std::vector const& changes, bool update_indexes) { using namespace schema_change; struct Applier { - Applier(Group& group, bool update_indexes) : group{group}, table{group}, update_indexes{update_indexes} { } + Applier(Group& group, bool update_indexes) + : group{group}, table{group}, update_indexes{update_indexes} { } Group& group; TableHelper table; bool update_indexes; void operator()(AddTable op) { create_table(group, *op.object); } + void operator()(RemoveTable) { } + void operator()(AddInitialProperties op) { add_initial_columns(group, *op.object); } void operator()(AddProperty op) { add_column(group, table(op.object), *op.property); } - void operator()(AddIndex op) { if (update_indexes) add_index(table(op.object), op.property->table_column); } + void operator()(AddIndex op) { if (update_indexes) table(op.object).add_search_index(op.property->table_column); } void operator()(RemoveIndex op) { if (update_indexes) table(op.object).remove_search_index(op.property->table_column); } void operator()(RemoveProperty) { } @@ -520,13 +637,15 @@ static void apply_pre_migration_changes(Group& group, std::vector TableHelper table; void operator()(AddTable op) { create_table(group, *op.object); } + void operator()(RemoveTable) { } + void operator()(AddInitialProperties op) { add_initial_columns(group, *op.object); } void operator()(AddProperty op) { add_column(group, table(op.object), *op.property); } void operator()(RemoveProperty) { /* delayed until after the migration */ } void operator()(ChangePropertyType op) { replace_column(group, table(op.object), *op.old_property, *op.new_property); } void operator()(MakePropertyNullable op) { make_property_optional(group, table(op.object), *op.property); } void operator()(MakePropertyRequired op) { make_property_required(group, table(op.object), *op.property); } - void operator()(ChangePrimaryKey op) { ObjectStore::set_primary_key_for_object(group, op.object->name, op.property ? op.property->name : ""); } - void operator()(AddIndex op) { add_index(table(op.object), op.property->table_column); } + void operator()(ChangePrimaryKey op) { ObjectStore::set_primary_key_for_object(group, op.object->name.c_str(), op.property ? op.property->name.c_str() : ""); } + void operator()(AddIndex op) { table(op.object).add_search_index(op.property->table_column); } void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->table_column); } } applier{group}; @@ -535,14 +654,21 @@ static void apply_pre_migration_changes(Group& group, std::vector } } -static void apply_post_migration_changes(Group& group, std::vector const& changes, Schema const& initial_schema) +enum class DidRereadSchema { Yes, No }; + +static void apply_post_migration_changes(Group& group, std::vector const& changes, Schema const& initial_schema, + DidRereadSchema did_reread_schema) { using namespace schema_change; struct Applier { - Applier(Group& group, Schema const& initial_schema) : group{group}, initial_schema(initial_schema), table(group) { } + Applier(Group& group, Schema const& initial_schema, DidRereadSchema did_reread_schema) + : group{group}, initial_schema(initial_schema), table(group) + , did_reread_schema(did_reread_schema == DidRereadSchema::Yes) + { } Group& group; Schema const& initial_schema; TableHelper table; + bool did_reread_schema; void operator()(RemoveProperty op) { @@ -560,113 +686,169 @@ static void apply_post_migration_changes(Group& group, std::vector } void operator()(AddTable op) { create_table(group, *op.object); } - void operator()(AddIndex op) { add_index(table(op.object), op.property->table_column); } + + void operator()(AddInitialProperties op) { + if (did_reread_schema) + add_initial_columns(group, *op.object); + else { + // If we didn't re-read the schema then AddInitialProperties was already taken care of + // during apply_pre_migration_changes. + } + } + + void operator()(AddIndex op) { table(op.object).add_search_index(op.property->table_column); } void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->table_column); } + void operator()(RemoveTable) { } void operator()(ChangePropertyType) { } void operator()(MakePropertyNullable) { } void operator()(MakePropertyRequired) { } void operator()(AddProperty) { } - } applier{group, initial_schema}; + } applier{group, initial_schema, did_reread_schema}; for (auto& change : changes) { change.visit(applier); } } -void ObjectStore::apply_schema_changes(Group& group, Schema& schema, uint64_t& schema_version, - Schema const& target_schema, uint64_t target_schema_version, - SchemaMode mode, std::vector const& changes, - std::function migration_function) +static void create_default_permissions(Group& group, std::vector const& changes, + std::string const& sync_user_id) { - create_metadata_tables(group); +#if !REALM_ENABLE_SYNC + static_cast(group); + static_cast(changes); + static_cast(sync_user_id); +#else + sync::set_up_basic_permissions(group, true); + + // Ensure that this user exists so that local privileges checks work immediately + sync::add_user_to_role(group, sync_user_id, "everyone"); + + // Ensure that the user's private role exists so that local privilege checks work immediately. + ObjectStore::ensure_private_role_exists_for_user(group, sync_user_id); + + // Mark all tables we just created as fully world-accessible + // This has to be done after the first pass of schema init is done so that we can be + // sure that the permissions tables actually exist. + using namespace schema_change; + struct Applier { + Group& group; + void operator()(AddTable op) + { + sync::set_class_permissions_for_role(group, op.object->name, "everyone", + static_cast(ComputedPrivileges::All)); + } - if (schema_version == ObjectStore::NotVersioned) { - create_initial_tables(group, changes); - set_schema_version(group, target_schema_version); - schema_version = target_schema_version; - schema = target_schema; - set_schema_columns(group, schema); + void operator()(RemoveTable) { } + void operator()(AddInitialProperties) { } + void operator()(AddProperty) { } + void operator()(RemoveProperty) { } + void operator()(MakePropertyNullable) { } + void operator()(MakePropertyRequired) { } + void operator()(ChangePrimaryKey) { } + void operator()(AddIndex) { } + void operator()(RemoveIndex) { } + void operator()(ChangePropertyType) { } + } applier{group}; + + for (auto& change : changes) { + change.visit(applier); + } +#endif +} + +#if REALM_ENABLE_SYNC +void ObjectStore::ensure_private_role_exists_for_user(Group& group, StringData sync_user_id) +{ + std::string private_role_name = util::format("__User:%1", sync_user_id); + + TableRef roles = ObjectStore::table_for_object_type(group, "__Role"); + size_t private_role_ndx = roles->find_first_string(roles->get_column_index("name"), private_role_name); + if (private_role_ndx != npos) { + // The private role already exists, so there's nothing for us to do. return; } + // Add the user to the private role, creating the private role in the process. + sync::add_user_to_role(group, sync_user_id, private_role_name); + + // Set the private role on the user. + private_role_ndx = roles->find_first_string(roles->get_column_index("name"), private_role_name); + TableRef users = ObjectStore::table_for_object_type(group, "__User"); + size_t user_ndx = users->find_first_string(users->get_column_index("id"), sync_user_id); + users->set_link(users->get_column_index("role"), user_ndx, private_role_ndx); +} +#endif + +void ObjectStore::apply_schema_changes(Group& group, uint64_t schema_version, + Schema& target_schema, uint64_t target_schema_version, + SchemaMode mode, std::vector const& changes, + util::Optional sync_user_id, + std::function migration_function) +{ + create_metadata_tables(group, sync_user_id != util::none); + if (mode == SchemaMode::Additive) { - apply_additive_changes(group, changes, schema_version < target_schema_version); + bool target_schema_is_newer = (schema_version < target_schema_version + || schema_version == ObjectStore::NotVersioned); - if (schema_version < target_schema_version) { - schema_version = target_schema_version; + // With sync v2.x, indexes are no longer synced, so there's no reason to avoid creating them. + bool update_indexes = true; + apply_additive_changes(group, changes, update_indexes); + + if (target_schema_is_newer) set_schema_version(group, target_schema_version); - } - schema = target_schema; - set_schema_columns(group, schema); + if (sync_user_id) + create_default_permissions(group, changes, *sync_user_id); + + set_schema_columns(group, target_schema); + return; + } + + if (schema_version == ObjectStore::NotVersioned) { + create_initial_tables(group, changes); + set_schema_version(group, target_schema_version); + set_schema_columns(group, target_schema); return; } if (mode == SchemaMode::Manual) { - // Have to update the schema on the Realm before calling the migration - // function as the migration will need it - auto old_version = schema_version; - auto old_schema = schema; - schema_version = target_schema_version; - schema = target_schema; - set_schema_columns(group, schema); - - try { + set_schema_columns(group, target_schema); + if (migration_function) { migration_function(); - verify_no_changes_required(schema_from_group(group).compare(schema)); - validate_primary_column_uniqueness(group); - } - catch (...) { - schema = move(old_schema); - schema_version = old_version; - throw; } - set_schema_columns(group, schema); + verify_no_changes_required(schema_from_group(group).compare(target_schema)); + validate_primary_column_uniqueness(group); + set_schema_columns(group, target_schema); set_schema_version(group, target_schema_version); return; } if (schema_version == target_schema_version) { apply_non_migration_changes(group, changes); - schema = target_schema; - set_schema_columns(group, schema); + set_schema_columns(group, target_schema); return; } + auto old_schema = schema_from_group(group); apply_pre_migration_changes(group, changes); if (migration_function) { - // Have to update the schema on the Realm before calling the migration - // function as the migration will need it - auto old_version = schema_version; - auto old_schema = schema; - schema_version = target_schema_version; - schema = target_schema; - set_schema_columns(group, schema); - - try { - migration_function(); + set_schema_columns(group, target_schema); + migration_function(); - // Migration function may have changed the schema, so we need to re-read it - schema = schema_from_group(group); - apply_post_migration_changes(group, schema.compare(target_schema), old_schema); - validate_primary_column_uniqueness(group); - } - catch (...) { - schema = move(old_schema); - schema_version = old_version; - throw; - } + // Migration function may have changed the schema, so we need to re-read it + auto schema = schema_from_group(group); + apply_post_migration_changes(group, schema.compare(target_schema), old_schema, DidRereadSchema::Yes); + validate_primary_column_uniqueness(group); } else { - apply_post_migration_changes(group, changes, {}); + apply_post_migration_changes(group, changes, {}, DidRereadSchema::No); } set_schema_version(group, target_schema_version); - schema_version = target_schema_version; - schema = target_schema; - set_schema_columns(group, schema); + set_schema_columns(group, target_schema); } Schema ObjectStore::schema_from_group(Group const& group) { @@ -681,6 +863,37 @@ Schema ObjectStore::schema_from_group(Group const& group) { return schema; } +util::Optional ObjectStore::property_for_column_index(ConstTableRef& table, size_t column_index) +{ + StringData column_name = table->get_column_name(column_index); + +#if REALM_ENABLE_SYNC + // The object ID column is an implementation detail, and is omitted from the schema. + // FIXME: Consider filtering out all column names starting with `!`. + if (column_name == sync::object_id_column_name) + return util::none; +#endif + + if (table->get_column_type(column_index) == type_Table) { + auto subdesc = table->get_subdescriptor(column_index); + if (subdesc->get_column_count() != 1 || subdesc->get_column_name(0) != ObjectStore::ArrayColumnName) + return util::none; + } + + Property property; + property.name = column_name; + property.type = ObjectSchema::from_core_type(*table->get_descriptor(), column_index); + property.is_indexed = table->has_search_index(column_index); + property.table_column = column_index; + + if (property.type == PropertyType::Object) { + // set link type for objects and arrays + ConstTableRef linkTable = table->get_link_target(column_index); + property.object_type = ObjectStore::object_type_for_table_name(linkTable->get_name().data()); + } + return property; +} + void ObjectStore::set_schema_columns(Group const& group, Schema& schema) { for (auto& object_schema : schema) { @@ -704,8 +917,8 @@ void ObjectStore::delete_data_for_object(Group& group, StringData object_type) { bool ObjectStore::is_empty(Group const& group) { for (size_t i = 0; i < group.size(); i++) { ConstTableRef table = group.get_table(i); - std::string object_type = object_type_for_table_name(table->get_name()); - if (!object_type.length()) { + auto object_type = object_type_for_table_name(table->get_name()); + if (object_type.size() == 0 || object_type.begins_with("__")) { continue; } if (!table->is_empty()) { @@ -753,7 +966,7 @@ void ObjectStore::rename_property(Group& group, Schema& target_schema, StringDat object_type, old_name, new_name, old_property->type_string(), new_property->type_string())); } - if (old_property->is_nullable && !new_property->is_nullable) { + if (is_nullable(old_property->type) && !is_nullable(new_property->type)) { throw std::logic_error(util::format("Cannot rename property '%1.%2' to '%3' because it would change from optional to required.", object_type, old_name, new_name)); } @@ -771,7 +984,7 @@ void ObjectStore::rename_property(Group& group, Schema& target_schema, StringDat } // update nullability for column - if (new_property->is_nullable && !old_property->is_nullable) { + if (is_nullable(new_property->type) && !is_nullable(old_property->type)) { auto prop = *new_property; prop.table_column = old_property->table_column; make_property_optional(group, *table, prop); @@ -822,3 +1035,17 @@ InvalidSchemaChangeException::InvalidSchemaChangeException(std::vector const& errors) +: std::logic_error([&] { + std::string message = + "Unsupported schema changes were made by another client or process. For a " + "synchronized Realm, this may be due to the server reverting schema changes which " + "the local user did not have permission to make."; + for (auto const& error : errors) { + message += std::string("\n- ") + error.what(); + } + return message; +}()) +{ +} diff --git a/Pods/Realm/Realm/ObjectStore/src/results.cpp b/Pods/Realm/Realm/ObjectStore/src/results.cpp index 0f96642..f99ad4f 100644 --- a/Pods/Realm/Realm/ObjectStore/src/results.cpp +++ b/Pods/Realm/Realm/ObjectStore/src/results.cpp @@ -23,54 +23,50 @@ #include "object_schema.hpp" #include "object_store.hpp" #include "schema.hpp" -#include "util/compiler.hpp" -#include "util/format.hpp" #include -using namespace realm; +namespace realm { Results::Results() = default; Results::~Results() = default; -Results::Results(SharedRealm r, Query q, SortDescriptor s, SortDescriptor d) +Results::Results(SharedRealm r, Query q, DescriptorOrdering o) : m_realm(std::move(r)) , m_query(std::move(q)) -, m_table(m_query.get_table().get()) -, m_sort(std::move(s)) -, m_distinct(std::move(d)) +, m_table(m_query.get_table()) +, m_descriptor_ordering(std::move(o)) , m_mode(Mode::Query) { } Results::Results(SharedRealm r, Table& table) : m_realm(std::move(r)) -, m_table(&table) , m_mode(Mode::Table) { + m_table.reset(&table); } Results::Results(SharedRealm r, LinkViewRef lv, util::Optional q, SortDescriptor s) : m_realm(std::move(r)) , m_link_view(lv) -, m_table(&lv->get_target_table()) -, m_sort(std::move(s)) , m_mode(Mode::LinkView) { + m_table.reset(&lv->get_target_table()); if (q) { m_query = std::move(*q); m_mode = Mode::Query; } + m_descriptor_ordering.append_sort(std::move(s)); } -Results::Results(SharedRealm r, TableView tv, SortDescriptor s, SortDescriptor d) +Results::Results(SharedRealm r, TableView tv, DescriptorOrdering o) : m_realm(std::move(r)) , m_table_view(std::move(tv)) -, m_table(&m_table_view.get_parent()) -, m_sort(std::move(s)) -, m_distinct(std::move(d)) +, m_descriptor_ordering(std::move(o)) , m_mode(Mode::TableView) { + m_table.reset(&m_table_view.get_parent()); } Results::Results(const Results&) = default; @@ -82,9 +78,8 @@ Results::Results(Results&& other) , m_query(std::move(other.m_query)) , m_table_view(std::move(other.m_table_view)) , m_link_view(std::move(other.m_link_view)) -, m_table(other.m_table) -, m_sort(std::move(other.m_sort)) -, m_distinct(std::move(other.m_distinct)) +, m_table(std::move(other.m_table)) +, m_descriptor_ordering(std::move(other.m_descriptor_ordering)) , m_notifier(std::move(other.m_notifier)) , m_mode(other.m_mode) , m_update_policy(other.m_update_policy) @@ -137,14 +132,14 @@ size_t Results::size() case Mode::LinkView: return m_link_view->size(); case Mode::Query: m_query.sync_view_if_needed(); - if (!m_distinct) + if (!m_descriptor_ordering.will_apply_distinct()) return m_query.count(); REALM_FALLTHROUGH; case Mode::TableView: - update_tableview(); + evaluate_query_if_needed(); return m_table_view.size(); } - REALM_UNREACHABLE(); + REALM_COMPILER_HINT_UNREACHABLE(); } const ObjectSchema& Results::get_object_schema() const @@ -171,89 +166,86 @@ StringData Results::get_object_type() const noexcept return ObjectStore::object_type_for_table_name(m_table->get_name()); } -RowExpr Results::get(size_t row_ndx) +namespace { +template +auto get(Table& table, size_t row) +{ + return table.get(0, row); +} + +template<> +auto get(Table& table, size_t row) +{ + return table.get(row); +} +} + +template +util::Optional Results::try_get(size_t row_ndx) { validate_read(); switch (m_mode) { case Mode::Empty: break; case Mode::Table: if (row_ndx < m_table->size()) - return m_table->get(row_ndx); + return realm::get(*m_table, row_ndx); break; case Mode::LinkView: if (update_linkview()) { if (row_ndx < m_link_view->size()) - return m_link_view->get(row_ndx); + return realm::get(*m_table, m_link_view->get(row_ndx).get_index()); break; } REALM_FALLTHROUGH; case Mode::Query: case Mode::TableView: - update_tableview(); + evaluate_query_if_needed(); if (row_ndx >= m_table_view.size()) break; if (m_update_policy == UpdatePolicy::Never && !m_table_view.is_row_attached(row_ndx)) - return {}; - return m_table_view.get(row_ndx); + return T{}; + return realm::get(*m_table, m_table_view.get(row_ndx).get_index()); } + return util::none; +} +template +T Results::get(size_t row_ndx) +{ + if (auto row = try_get(row_ndx)) + return *row; throw OutOfBoundsIndexException{row_ndx, size()}; } -util::Optional Results::first() +template +util::Optional Results::first() { - validate_read(); - switch (m_mode) { - case Mode::Empty: - return none; - case Mode::Table: - return m_table->size() == 0 ? util::none : util::make_optional(m_table->front()); - case Mode::LinkView: - if (update_linkview()) - return m_link_view->size() == 0 ? util::none : util::make_optional(m_link_view->get(0)); - REALM_FALLTHROUGH; - case Mode::Query: - case Mode::TableView: - update_tableview(); - return m_table_view.size() == 0 ? util::none : util::make_optional(m_table_view.front()); - } - REALM_UNREACHABLE(); + return try_get(0); } -util::Optional Results::last() +template +util::Optional Results::last() { validate_read(); - switch (m_mode) { - case Mode::Empty: - return none; - case Mode::Table: - return m_table->size() == 0 ? util::none : util::make_optional(m_table->back()); - case Mode::LinkView: - if (update_linkview()) - return m_link_view->size() == 0 ? util::none : util::make_optional(m_link_view->get(m_link_view->size() - 1)); - REALM_FALLTHROUGH; - case Mode::Query: - case Mode::TableView: - update_tableview(); - return m_table_view.size() == 0 ? util::none : util::make_optional(m_table_view.back()); - } - REALM_UNREACHABLE(); + if (m_mode == Mode::Query) + evaluate_query_if_needed(); // avoid running the query twice (for size() and for get()) + return try_get(size() - 1); } bool Results::update_linkview() { REALM_ASSERT(m_update_policy == UpdatePolicy::Auto); - if (m_sort || m_distinct) { + if (!m_descriptor_ordering.is_empty()) { m_query = get_query(); m_mode = Mode::Query; - update_tableview(); + evaluate_query_if_needed(); return false; } return true; } -void Results::update_tableview(bool wants_notifications) +void Results::evaluate_query_if_needed(bool wants_notifications) { if (m_update_policy == UpdatePolicy::Never) { REALM_ASSERT(m_mode == Mode::TableView); @@ -268,26 +260,22 @@ void Results::update_tableview(bool wants_notifications) case Mode::Query: m_query.sync_view_if_needed(); m_table_view = m_query.find_all(); - if (m_sort) { - m_table_view.sort(m_sort); - } - if (m_distinct) { - m_table_view.distinct(m_distinct); + if (!m_descriptor_ordering.is_empty()) { + m_table_view.apply_descriptor_ordering(m_descriptor_ordering); } m_mode = Mode::TableView; REALM_FALLTHROUGH; case Mode::TableView: - if (wants_notifications && !m_notifier && !m_realm->is_in_transaction() && m_realm->can_deliver_notifications()) { - m_notifier = std::make_shared<_impl::ResultsNotifier>(*this); - _impl::RealmCoordinator::register_notifier(m_notifier); - } + if (wants_notifications) + prepare_async(ForCallback{false}); m_has_used_table_view = true; m_table_view.sync_if_needed(); break; } } -size_t Results::index_of(Row const& row) +template<> +size_t Results::index_of(RowExpr const& row) { validate_read(); if (!row) { @@ -300,27 +288,78 @@ size_t Results::index_of(Row const& row) "Attempting to get the index of a Row of the wrong type" ); } - return index_of(row.get_index()); + + switch (m_mode) { + case Mode::Empty: + return not_found; + case Mode::Table: + return row.get_index(); + case Mode::LinkView: + if (update_linkview()) + return m_link_view->find(row.get_index()); + REALM_FALLTHROUGH; + case Mode::Query: + case Mode::TableView: + evaluate_query_if_needed(); + return m_table_view.find_by_source_ndx(row.get_index()); + } + REALM_COMPILER_HINT_UNREACHABLE(); } -size_t Results::index_of(size_t row_ndx) +template +size_t Results::index_of(T const& value) { validate_read(); switch (m_mode) { case Mode::Empty: return not_found; case Mode::Table: - return row_ndx; + return m_table->find_first(0, value); case Mode::LinkView: - if (update_linkview()) - return m_link_view->find(row_ndx); + REALM_UNREACHABLE(); + case Mode::Query: + case Mode::TableView: + evaluate_query_if_needed(); + return m_table_view.find_first(0, value); + } + REALM_COMPILER_HINT_UNREACHABLE(); +} + +size_t Results::index_of(Query&& q) +{ + if (m_descriptor_ordering.will_apply_sort()) { + auto first = filter(std::move(q)).first(); + return first ? index_of(*first) : not_found; + } + + auto query = get_query().and_query(std::move(q)); + query.sync_view_if_needed(); + size_t row = query.find(); + return row != not_found ? index_of(m_table->get(row)) : row; +} + +void Results::prepare_for_aggregate(size_t column, const char* name) +{ + if (column > m_table->get_column_count()) + throw OutOfBoundsIndexException{column, m_table->get_column_count()}; + switch (m_mode) { + case Mode::Empty: break; + case Mode::Table: break; + case Mode::LinkView: + m_query = this->get_query(); + m_mode = Mode::Query; REALM_FALLTHROUGH; case Mode::Query: case Mode::TableView: - update_tableview(); - return m_table_view.find_by_source_ndx(row_ndx); + evaluate_query_if_needed(); + break; + default: + REALM_COMPILER_HINT_UNREACHABLE(); + } + switch (m_table->get_column_type(column)) { + case type_Timestamp: case type_Double: case type_Float: case type_Int: break; + default: throw UnsupportedColumnTypeException{column, m_table.get(), name}; } - REALM_UNREACHABLE(); } template @@ -332,36 +371,17 @@ util::Optional Results::aggregate(size_t column, validate_read(); if (!m_table) return none; - if (column > m_table->get_column_count()) - throw OutOfBoundsIndexException{column, m_table->get_column_count()}; - - auto do_agg = [&](auto const& getter) -> util::Optional { - switch (m_mode) { - case Mode::Empty: - return none; - case Mode::Table: - return util::Optional(getter(*m_table)); - case Mode::LinkView: - m_query = this->get_query(); - m_mode = Mode::Query; - REALM_FALLTHROUGH; - case Mode::Query: - case Mode::TableView: - this->update_tableview(); - return util::Optional(getter(m_table_view)); - } + prepare_for_aggregate(column, name); - REALM_UNREACHABLE(); + auto do_agg = [&](auto const& getter) { + return Mixed(m_mode == Mode::Table ? getter(*m_table) : getter(m_table_view)); }; - - switch (m_table->get_column_type(column)) - { + switch (m_table->get_column_type(column)) { case type_Timestamp: return do_agg(agg_timestamp); - case type_Double: return do_agg(agg_double); - case type_Float: return do_agg(agg_float); - case type_Int: return do_agg(agg_int); - default: - throw UnsupportedColumnTypeException{column, m_table, name}; + case type_Double: return do_agg(agg_double); + case type_Float: return do_agg(agg_float); + case type_Int: return do_agg(agg_int); + default: REALM_COMPILER_HINT_UNREACHABLE(); } } @@ -393,19 +413,18 @@ util::Optional Results::sum(size_t column) [=](auto const& table) { return table.sum_int(column); }, [=](auto const& table) { return table.sum_float(column); }, [=](auto const& table) { return table.sum_double(column); }, - [=](auto const&) -> util::None { throw UnsupportedColumnTypeException{column, m_table, "sum"}; }); + [=](auto const&) -> Timestamp { throw UnsupportedColumnTypeException{column, m_table.get(), "sum"}; }); } -util::Optional Results::average(size_t column) +util::Optional Results::average(size_t column) { - // Initial value to make gcc happy size_t value_count = 0; auto results = aggregate(column, "average", [&](auto const& table) { return table.average_int(column, &value_count); }, [&](auto const& table) { return table.average_float(column, &value_count); }, [&](auto const& table) { return table.average_double(column, &value_count); }, - [&](auto const&) -> util::None { throw UnsupportedColumnTypeException{column, m_table, "average"}; }); - return value_count == 0 ? none : results; + [&](auto const&) -> Timestamp { throw UnsupportedColumnTypeException{column, m_table.get(), "average"}; }); + return value_count == 0 ? none : util::make_optional(results->get_double()); } void Results::clear() @@ -415,14 +434,17 @@ void Results::clear() return; case Mode::Table: validate_write(); - m_table->clear(); + if (m_realm->is_partial()) + Results(m_realm, m_table->where()).clear(); + else + m_table->clear(); break; case Mode::Query: // Not using Query:remove() because building the tableview and // clearing it is actually significantly faster case Mode::TableView: validate_write(); - update_tableview(); + evaluate_query_if_needed(); switch (m_update_policy) { case UpdatePolicy::Auto: @@ -443,6 +465,23 @@ void Results::clear() } } +PropertyType Results::get_type() const +{ + validate_read(); + switch (m_mode) { + case Mode::Empty: + case Mode::LinkView: + return PropertyType::Object; + case Mode::Query: + case Mode::TableView: + case Mode::Table: + if (m_table->get_index_in_group() != npos) + return PropertyType::Object; + return ObjectSchema::from_core_type(*m_table->get_descriptor(), 0); + } + REALM_COMPILER_HINT_UNREACHABLE(); +} + Query Results::get_query() const { validate_read(); @@ -470,7 +509,7 @@ Query Results::get_query() const case Mode::Table: return m_table->where(); } - REALM_UNREACHABLE(); + REALM_COMPILER_HINT_UNREACHABLE(); } TableView Results::get_tableview() @@ -485,40 +524,144 @@ TableView Results::get_tableview() REALM_FALLTHROUGH; case Mode::Query: case Mode::TableView: - update_tableview(); + evaluate_query_if_needed(); return m_table_view; case Mode::Table: return m_table->where().find_all(); } - REALM_UNREACHABLE(); + REALM_COMPILER_HINT_UNREACHABLE(); } -Results Results::sort(realm::SortDescriptor&& sort) const +static std::vector parse_keypath(StringData keypath, Schema const& schema, const ObjectSchema *object_schema) { - return Results(m_realm, get_query(), std::move(sort), m_distinct); + auto check = [&](bool condition, const char* fmt, auto... args) { + if (!condition) { + throw std::invalid_argument(util::format("Cannot sort on key path '%1': %2.", + keypath, util::format(fmt, args...))); + } + }; + auto is_sortable_type = [](PropertyType type) { + return !is_array(type) && type != PropertyType::LinkingObjects && type != PropertyType::Data; + }; + + const char* begin = keypath.data(); + const char* end = keypath.data() + keypath.size(); + check(begin != end, "missing property name"); + + std::vector indices; + while (begin != end) { + auto sep = std::find(begin, end, '.'); + check(sep != begin && sep + 1 != end, "missing property name"); + StringData key(begin, sep - begin); + begin = sep + (sep != end); + + auto prop = object_schema->property_for_name(key); + check(prop, "property '%1.%2' does not exist", object_schema->name, key); + check(is_sortable_type(prop->type), "property '%1.%2' is of unsupported type '%3'", + object_schema->name, key, string_for_property_type(prop->type)); + if (prop->type == PropertyType::Object) + check(begin != end, "property '%1.%2' of type 'object' cannot be the final property in the key path", + object_schema->name, key); + else + check(begin == end, "property '%1.%2' of type '%3' may only be the final property in the key path", + object_schema->name, key, prop->type_string()); + + indices.push_back(prop->table_column); + if (prop->type == PropertyType::Object) + object_schema = &*schema.find(prop->object_type); + } + return indices; +} + +Results Results::sort(std::vector> const& keypaths) const +{ + if (keypaths.empty()) + return *this; + if (get_type() != PropertyType::Object) { + if (keypaths.size() != 1) + throw std::invalid_argument(util::format("Cannot sort array of '%1' on more than one key path", + string_for_property_type(get_type()))); + if (keypaths[0].first != "self") + throw std::invalid_argument(util::format("Cannot sort on key path '%1': arrays of '%2' can only be sorted on 'self'", + keypaths[0].first, string_for_property_type(get_type()))); + return sort({*m_table, {{0}}, {keypaths[0].second}}); + } + + std::vector> column_indices; + std::vector ascending; + column_indices.reserve(keypaths.size()); + ascending.reserve(keypaths.size()); + + for (auto& keypath : keypaths) { + column_indices.push_back(parse_keypath(keypath.first, m_realm->schema(), &get_object_schema())); + ascending.push_back(keypath.second); + } + return sort({*m_table, std::move(column_indices), std::move(ascending)}); +} + +Results Results::sort(SortDescriptor&& sort) const +{ + if (m_mode == Mode::LinkView) + return Results(m_realm, m_link_view, util::none, std::move(sort)); + DescriptorOrdering new_order = m_descriptor_ordering; + new_order.append_sort(std::move(sort)); + return Results(m_realm, get_query(), std::move(new_order)); } Results Results::filter(Query&& q) const { - return Results(m_realm, get_query().and_query(std::move(q)), m_sort, m_distinct); + return Results(m_realm, get_query().and_query(std::move(q)), m_descriptor_ordering); } +Results Results::apply_ordering(DescriptorOrdering&& ordering) +{ + DescriptorOrdering new_order = m_descriptor_ordering; + for (size_t i = 0; i < ordering.size(); ++i) { + const CommonDescriptor* desc = ordering[i]; + if (const SortDescriptor* sort = dynamic_cast(desc)) { + new_order.append_sort(std::move(*sort)); + continue; + } + if (const DistinctDescriptor* distinct = dynamic_cast(desc)) { + new_order.append_distinct(std::move(*distinct)); + continue; + } + REALM_COMPILER_HINT_UNREACHABLE(); + } + return Results(m_realm, get_query(), std::move(new_order)); +} -// FIXME: The current implementation of distinct() breaks the Results API. -// This is tracked by the following issues: -// - https://github.com/realm/realm-object-store/issues/266 -// - https://github.com/realm/realm-core/issues/2332 -Results Results::distinct(realm::SortDescriptor&& uniqueness) +Results Results::distinct(DistinctDescriptor&& uniqueness) const { - auto tv = get_tableview(); - tv.distinct(uniqueness); - return Results(m_realm, std::move(tv), m_sort, std::move(uniqueness)); + DescriptorOrdering new_order = m_descriptor_ordering; + new_order.append_distinct(std::move(uniqueness)); + return Results(m_realm, get_query(), std::move(new_order)); +} + +Results Results::distinct(std::vector const& keypaths) const +{ + if (keypaths.empty()) + return *this; + if (get_type() != PropertyType::Object) { + if (keypaths.size() != 1) + throw std::invalid_argument(util::format("Cannot sort array of '%1' on more than one key path", + string_for_property_type(get_type()))); + if (keypaths[0] != "self") + throw std::invalid_argument(util::format("Cannot sort on key path '%1': arrays of '%2' can only be sorted on 'self'", + keypaths[0], string_for_property_type(get_type()))); + return distinct({*m_table, {{0}}}); + } + + std::vector> column_indices; + column_indices.reserve(keypaths.size()); + for (auto& keypath : keypaths) + column_indices.push_back(parse_keypath(keypath, m_realm->schema(), &get_object_schema())); + return distinct({*m_table, std::move(column_indices)}); } Results Results::snapshot() const & { validate_read(); - return Results(*this).snapshot(); } @@ -538,27 +681,42 @@ Results Results::snapshot() && REALM_FALLTHROUGH; case Mode::Query: case Mode::TableView: - update_tableview(false); + evaluate_query_if_needed(false); m_notifier.reset(); m_update_policy = UpdatePolicy::Never; return std::move(*this); } - REALM_UNREACHABLE(); + REALM_COMPILER_HINT_UNREACHABLE(); } -void Results::prepare_async() +void Results::prepare_async(ForCallback force) { if (m_notifier) { return; } - if (m_realm->config().read_only()) { - throw InvalidTransactionException("Cannot create asynchronous query for read-only Realms"); + if (m_realm->config().immutable()) { + if (force) + throw InvalidTransactionException("Cannot create asynchronous query for immutable Realms"); + return; } if (m_realm->is_in_transaction()) { - throw InvalidTransactionException("Cannot create asynchronous query while in a write transaction"); + if (force) + throw InvalidTransactionException("Cannot create asynchronous query while in a write transaction"); + return; } if (m_update_policy == UpdatePolicy::Never) { - throw std::logic_error("Cannot create asynchronous query for snapshotted Results."); + if (force) + throw std::logic_error("Cannot create asynchronous query for snapshotted Results."); + return; + } + if (!force) { + // Don't do implicit background updates if we can't actually deliver them + if (!m_realm->can_deliver_notifications()) + return; + // Don't do implicit background updates if there isn't actually anything + // that needs to be run. + if (!m_query.get_table() && m_descriptor_ordering.is_empty()) + return; } m_wants_background_updates = true; @@ -566,16 +724,9 @@ void Results::prepare_async() _impl::RealmCoordinator::register_notifier(m_notifier); } -NotificationToken Results::async(std::function target) -{ - prepare_async(); - auto wrap = [=](CollectionChangeSet, std::exception_ptr e) { target(e); }; - return {m_notifier, m_notifier->add_callback(wrap)}; -} - NotificationToken Results::add_notification_callback(CollectionChangeCallback cb) & { - prepare_async(); + prepare_async(ForCallback{true}); return {m_notifier, m_notifier->add_callback(std::move(cb))}; } @@ -588,14 +739,14 @@ bool Results::is_in_table_order() const case Mode::LinkView: return false; case Mode::Query: - return m_query.produces_results_in_table_order() && !m_sort; + return m_query.produces_results_in_table_order() && !m_descriptor_ordering.will_apply_sort(); case Mode::TableView: return m_table_view.is_in_table_order(); } - REALM_UNREACHABLE(); // keep gcc happy + REALM_COMPILER_HINT_UNREACHABLE(); } -void Results::Internal::set_table_view(Results& results, realm::TableView &&tv) +void Results::Internal::set_table_view(Results& results, TableView &&tv) { REALM_ASSERT(results.m_update_policy != UpdatePolicy::Never); // If the previous TableView was never actually used, then stop generating @@ -611,16 +762,50 @@ void Results::Internal::set_table_view(Results& results, realm::TableView &&tv) REALM_ASSERT(results.m_table_view.is_attached()); } +#define REALM_RESULTS_TYPE(T) \ + template T Results::get(size_t); \ + template util::Optional Results::first(); \ + template util::Optional Results::last(); \ + template size_t Results::index_of(T const&); + +template RowExpr Results::get(size_t); +template util::Optional Results::first(); +template util::Optional Results::last(); + +REALM_RESULTS_TYPE(bool) +REALM_RESULTS_TYPE(int64_t) +REALM_RESULTS_TYPE(float) +REALM_RESULTS_TYPE(double) +REALM_RESULTS_TYPE(StringData) +REALM_RESULTS_TYPE(BinaryData) +REALM_RESULTS_TYPE(Timestamp) +REALM_RESULTS_TYPE(util::Optional) +REALM_RESULTS_TYPE(util::Optional) +REALM_RESULTS_TYPE(util::Optional) +REALM_RESULTS_TYPE(util::Optional) + +#undef REALM_RESULTS_TYPE + Results::OutOfBoundsIndexException::OutOfBoundsIndexException(size_t r, size_t c) -: std::out_of_range(util::format("Requested index %1 greater than max %2", r, c)) +: std::out_of_range(util::format("Requested index %1 greater than max %2", r, c - 1)) , requested(r), valid_count(c) {} +static std::string unsupported_operation_msg(size_t column, const Table* table, const char* operation) +{ + const char* column_type = string_for_property_type(ObjectSchema::from_core_type(*table->get_descriptor(), column)); + if (table->is_group_level()) + return util::format("Cannot %1 property '%2': operation not supported for '%3' properties", + operation, table->get_column_name(column), column_type); + return util::format("Cannot %1 '%2' array: operation not supported", + operation, column_type); +} + Results::UnsupportedColumnTypeException::UnsupportedColumnTypeException(size_t column, const Table* table, const char* operation) -: std::logic_error(util::format("Cannot %1 property '%2': operation not supported for '%3' properties", - operation, table->get_column_name(column), - string_for_property_type(static_cast(table->get_column_type(column))))) +: std::logic_error(unsupported_operation_msg(column, table, operation)) , column_index(column) , column_name(table->get_column_name(column)) -, column_type(table->get_column_type(column)) +, property_type(ObjectSchema::from_core_type(*table->get_descriptor(), column)) { } + +} // namespace realm diff --git a/Pods/Realm/Realm/ObjectStore/src/schema.cpp b/Pods/Realm/Realm/ObjectStore/src/schema.cpp index f928354..7916fee 100644 --- a/Pods/Realm/Realm/ObjectStore/src/schema.cpp +++ b/Pods/Realm/Realm/ObjectStore/src/schema.cpp @@ -79,6 +79,18 @@ Schema::const_iterator Schema::find(ObjectSchema const& object) const noexcept void Schema::validate() const { std::vector exceptions; + + auto find_next_duplicate = [&](const_iterator start) { + return std::adjacent_find(start, cend(), [](ObjectSchema const& lft, ObjectSchema const& rgt) { + return lft.name == rgt.name; + }); + }; + + for (auto it = find_next_duplicate(cbegin()); it != cend(); it = find_next_duplicate(++it)) { + exceptions.push_back(ObjectSchemaValidationException("Type '%1' appears more than once in the schema.", + it->name)); + } + for (auto const& object : *this) { object.validate(*this, exceptions); } @@ -116,12 +128,15 @@ static void compare(ObjectSchema const& existing_schema, changes.emplace_back(schema_change::RemoveProperty{&existing_schema, ¤t_prop}); continue; } - if (current_prop.type != target_prop->type || current_prop.object_type != target_prop->object_type) { + if (current_prop.type != target_prop->type || + current_prop.object_type != target_prop->object_type || + is_array(current_prop.type) != is_array(target_prop->type)) { + changes.emplace_back(schema_change::ChangePropertyType{&existing_schema, ¤t_prop, target_prop}); continue; } - if (current_prop.is_nullable != target_prop->is_nullable) { - if (current_prop.is_nullable) + if (is_nullable(current_prop.type) != is_nullable(target_prop->type)) { + if (is_nullable(current_prop.type)) changes.emplace_back(schema_change::MakePropertyRequired{&existing_schema, ¤t_prop}); else changes.emplace_back(schema_change::MakePropertyNullable{&existing_schema, ¤t_prop}); @@ -152,36 +167,75 @@ static void compare(ObjectSchema const& existing_schema, [](auto a, auto b) { return GetRemovedColumn()(a) > GetRemovedColumn()(b); }); } -std::vector Schema::compare(Schema const& target_schema) const +template +void Schema::zip_matching(T&& a, U&& b, Func&& func) +{ + size_t i = 0, j = 0; + while (i < a.size() && j < b.size()) { + auto& object_schema = a[i]; + auto& matching_schema = b[j]; + int cmp = object_schema.name.compare(matching_schema.name); + if (cmp == 0) { + func(&object_schema, &matching_schema); + ++i; + ++j; + } + else if (cmp < 0) { + func(&object_schema, nullptr); + ++i; + } + else { + func(nullptr, &matching_schema); + ++j; + } + } + for (; i < a.size(); ++i) + func(&a[i], nullptr); + for (; j < b.size(); ++j) + func(nullptr, &b[j]); + +} + +std::vector Schema::compare(Schema const& target_schema, bool include_table_removals) const { std::vector changes; - for (auto &object_schema : target_schema) { - auto matching_schema = find(object_schema); - if (matching_schema == end()) { - changes.emplace_back(schema_change::AddTable{&object_schema}); - continue; + + // Add missing tables + zip_matching(target_schema, *this, [&](const ObjectSchema* target, const ObjectSchema* existing) { + if (target && !existing) { + changes.emplace_back(schema_change::AddTable{target}); } + else if (include_table_removals && existing && !target) { + changes.emplace_back(schema_change::RemoveTable{existing}); + } + }); - ::compare(*matching_schema, object_schema, changes); - } + // Modify columns + zip_matching(target_schema, *this, [&](const ObjectSchema* target, const ObjectSchema* existing) { + if (target && existing) + ::compare(*existing, *target, changes); + else if (target) { + // Target is a new table -- add all properties + changes.emplace_back(schema_change::AddInitialProperties{target}); + } + // nothing for tables in existing but not target + }); return changes; } void Schema::copy_table_columns_from(realm::Schema const& other) { - for (auto& source_schema : other) { - auto matching_schema = find(source_schema); - if (matching_schema == end()) { - continue; - } + zip_matching(*this, other, [&](ObjectSchema* existing, const ObjectSchema* other) { + if (!existing || !other) + return; - for (auto& current_prop : source_schema.persisted_properties) { - auto target_prop = matching_schema->property_for_name(current_prop.name); + for (auto& current_prop : other->persisted_properties) { + auto target_prop = existing->property_for_name(current_prop.name); if (target_prop) { target_prop->table_column = current_prop.table_column; } } - } + }); } namespace realm { @@ -203,7 +257,9 @@ bool operator==(SchemaChange const& lft, SchemaChange const& rgt) REALM_SC_COMPARE(AddIndex, v.object, v.property) REALM_SC_COMPARE(AddProperty, v.object, v.property) + REALM_SC_COMPARE(AddInitialProperties, v.object) REALM_SC_COMPARE(AddTable, v.object) + REALM_SC_COMPARE(RemoveTable, v.object) REALM_SC_COMPARE(ChangePrimaryKey, v.object, v.property) REALM_SC_COMPARE(ChangePropertyType, v.object, v.old_property, v.new_property) REALM_SC_COMPARE(MakePropertyNullable, v.object, v.property) diff --git a/Pods/Realm/Realm/ObjectStore/src/shared_realm.cpp b/Pods/Realm/Realm/ObjectStore/src/shared_realm.cpp index fb322f2..f40c4b0 100644 --- a/Pods/Realm/Realm/ObjectStore/src/shared_realm.cpp +++ b/Pods/Realm/Realm/ObjectStore/src/shared_realm.cpp @@ -31,50 +31,27 @@ #include "schema.hpp" #include "thread_safe_reference.hpp" -#include "util/compiler.hpp" -#include "util/format.hpp" - #include #include #if REALM_ENABLE_SYNC +#include "sync/impl/sync_file.hpp" +#include "sync/sync_config.hpp" +#include "sync/sync_manager.hpp" + #include +#include +#else +namespace realm { +namespace sync { + struct PermissionsCache {}; +} +} #endif using namespace realm; using namespace realm::_impl; -static std::string get_initial_temporary_directory() -{ - auto tmp_dir = getenv("TMPDIR"); - if (!tmp_dir) { - return std::string(); - } - std::string tmp_dir_str(tmp_dir); - if (!tmp_dir_str.empty() && tmp_dir_str.back() != '/') { - tmp_dir_str += '/'; - } - return tmp_dir_str; -} - -static std::string temporary_directory = get_initial_temporary_directory(); - -void realm::set_temporary_directory(std::string directory_path) -{ - if (directory_path.empty()) { - throw std::invalid_argument("'directory_path` is empty."); - } - if (directory_path.back() != '/') { - throw std::invalid_argument("'directory_path` must ends with '/'."); - } - temporary_directory = std::move(directory_path); -} - -const std::string& realm::get_temporary_directory() noexcept -{ - return temporary_directory; -} - Realm::Realm(Config config, std::shared_ptr<_impl::RealmCoordinator> coordinator) : m_config(std::move(config)) , m_execution_context(m_config.execution_context) @@ -83,29 +60,33 @@ Realm::Realm(Config config, std::shared_ptr<_impl::RealmCoordinator> coordinator if (m_read_only_group) { m_group = m_read_only_group.get(); - } - - // if there is an existing realm at the current path steal its schema/column mapping - if (auto existing = coordinator ? coordinator->get_schema() : nullptr) { - m_schema = *existing; - m_schema_version = coordinator->get_schema_version(); - } - else { - // otherwise get the schema from the group - m_schema_version = ObjectStore::get_schema_version(read_group()); - m_schema = ObjectStore::schema_from_group(read_group()); - - if (m_shared_group) { - m_schema_transaction_version = m_shared_group->get_version_of_current_transaction().version; - m_shared_group->end_read(); - m_group = nullptr; + m_schema_version = ObjectStore::get_schema_version(*m_group); + m_schema = ObjectStore::schema_from_group(*m_group); + } + else if (!coordinator || !coordinator->get_cached_schema(m_schema, m_schema_version, m_schema_transaction_version)) { + if (m_config.should_compact_on_launch_function) { + size_t free_space = -1; + size_t used_space = -1; + // getting stats requires committing a write transaction beforehand. + Group* group = nullptr; + if (m_shared_group->try_begin_write(group)) { + m_shared_group->commit(); + m_shared_group->get_stats(free_space, used_space); + if (m_config.should_compact_on_launch_function(free_space + used_space, used_space)) + compact(); + } } + read_group(); + if (coordinator) + coordinator->cache_schema(m_schema, m_schema_version, m_schema_transaction_version); + m_shared_group->end_read(); + m_group = nullptr; } m_coordinator = std::move(coordinator); } -REALM_NOINLINE static void translate_file_exception(StringData path, bool read_only=false) +REALM_NOINLINE static void translate_file_exception(StringData path, bool immutable=false) { try { throw; @@ -113,7 +94,7 @@ REALM_NOINLINE static void translate_file_exception(StringData path, bool read_o catch (util::File::PermissionDenied const& ex) { throw RealmFileException(RealmFileException::Kind::PermissionDenied, ex.get_path(), util::format("Unable to open a realm at path '%1'. Please use a path where your app has %2 permissions.", - ex.get_path(), read_only ? "read" : "read-write"), + ex.get_path(), immutable ? "read" : "read-write"), ex.what()); } catch (util::File::Exists const& ex) { @@ -157,14 +138,23 @@ REALM_NOINLINE static void translate_file_exception(StringData path, bool read_o } } +#if REALM_ENABLE_SYNC +static bool is_nonupgradable_history(IncompatibleHistories const& ex) +{ + // FIXME: Replace this with a proper specific exception type once Core adds support for it. + return ex.what() == std::string("Incompatible histories. Nonupgradable history schema"); +} +#endif + void Realm::open_with_config(const Config& config, std::unique_ptr& history, std::unique_ptr& shared_group, std::unique_ptr& read_only_group, Realm* realm) { + bool server_synchronization_mode = bool(config.sync_config) || config.force_sync_history; try { - if (config.read_only()) { + if (config.immutable()) { if (config.realm_data.is_null()) { read_only_group = std::make_unique(config.path, config.encryption_key.data(), Group::mode_ReadOnly); } @@ -174,10 +164,9 @@ void Realm::open_with_config(const Config& config, } } else { - bool server_synchronization_mode = bool(config.sync_config) || config.force_sync_history; if (server_synchronization_mode) { #if REALM_ENABLE_SYNC - history = realm::sync::make_sync_history(config.path); + history = realm::sync::make_client_history(config.path); #else REALM_TERMINATE("Realm was not built with sync enabled"); #endif @@ -198,19 +187,37 @@ void Realm::open_with_config(const Config& config, realm->upgrade_final_version = to_version; } }; - options.temp_dir = get_temporary_directory(); shared_group = std::make_unique(*history, options); } } - catch (realm::FileFormatUpgradeRequired const& ex) { + catch (realm::FileFormatUpgradeRequired const&) { if (config.schema_mode != SchemaMode::ResetFile) { - translate_file_exception(config.path, config.read_only()); + translate_file_exception(config.path, config.immutable()); } util::File::remove(config.path); open_with_config(config, history, shared_group, read_only_group, realm); } +#if REALM_ENABLE_SYNC + catch (IncompatibleHistories const& ex) { + if (!server_synchronization_mode || !is_nonupgradable_history(ex)) + translate_file_exception(config.path, config.immutable()); // Throws + + // Move the Realm file into the recovery directory. + std::string recovery_directory = SyncManager::shared().recovery_directory_path(); + std::string new_realm_path = util::reserve_unique_file_name(recovery_directory, "synced-realm-XXXXXXX"); + util::File::move(config.path, new_realm_path); + + const char* message = "The local copy of this synced Realm was created with an incompatible version of " + "Realm. It has been moved aside, and the Realm will be re-downloaded the next time it " + "is opened. You should write a handler for this error that uses the provided " + "configuration to open the old Realm in read-only mode to recover any pending changes " + "and then remove the Realm file."; + throw RealmFileException(RealmFileException::Kind::IncompatibleSyncedRealm, std::move(new_realm_path), + message, ex.what()); + } +#endif // REALM_ENABLE_SYNC catch (...) { - translate_file_exception(config.path, config.read_only()); + translate_file_exception(config.path, config.immutable()); } } @@ -221,22 +228,35 @@ Realm::~Realm() } } +bool Realm::is_partial() const noexcept +{ +#if REALM_ENABLE_SYNC + return m_config.sync_config && m_config.sync_config->is_partial; +#else + return false; +#endif +} + Group& Realm::read_group() { verify_open(); - if (!m_group) { - m_group = &const_cast(m_shared_group->begin_read()); - add_schema_change_handler(); - } + if (!m_group) + begin_read(VersionID{}); return *m_group; } void Realm::Internal::begin_read(Realm& realm, VersionID version_id) { - REALM_ASSERT(!realm.m_group); - realm.m_group = &const_cast(realm.m_shared_group->begin_read(version_id)); - realm.add_schema_change_handler(); + realm.begin_read(version_id); +} + +void Realm::begin_read(VersionID version_id) +{ + REALM_ASSERT(!m_group); + m_group = &const_cast(m_shared_group->begin_read(version_id)); + add_schema_change_handler(); + read_schema_from_group_if_needed(); } SharedRealm Realm::get_shared_realm(Config config) @@ -245,43 +265,51 @@ SharedRealm Realm::get_shared_realm(Config config) return coordinator->get_realm(std::move(config)); } -void Realm::set_schema(Schema schema, uint64_t version) +void Realm::set_schema(Schema const& reference, Schema schema) { - schema.copy_table_columns_from(m_schema); - m_schema = schema; - m_coordinator->update_schema(schema, version); + m_dynamic_schema = false; + schema.copy_table_columns_from(reference); + m_schema = std::move(schema); + notify_schema_changed(); } -bool Realm::read_schema_from_group_if_needed() +void Realm::read_schema_from_group_if_needed() { - // schema of read-only Realms can't change - if (m_read_only_group) - return false; + REALM_ASSERT(!m_read_only_group); Group& group = read_group(); auto current_version = m_shared_group->get_version_of_current_transaction().version; if (m_schema_transaction_version == current_version) - return false; + return; - m_schema = ObjectStore::schema_from_group(group); - m_schema_version = ObjectStore::get_schema_version(group); m_schema_transaction_version = current_version; - return true; -} - -bool Realm::reset_file_if_needed(Schema& schema, uint64_t version, std::vector& required_changes) -{ - if (m_schema_version == ObjectStore::NotVersioned) - return false; - if (m_schema_version == version) { - if (required_changes.empty()) { - set_schema(std::move(schema), version); - return true; + m_schema_version = ObjectStore::get_schema_version(group); + auto schema = ObjectStore::schema_from_group(group); + if (m_coordinator) + m_coordinator->cache_schema(schema, m_schema_version, + m_schema_transaction_version); + + if (m_dynamic_schema) { + if (m_schema == schema) { + // The structure of the schema hasn't changed. Bring the table column indices up to date. + m_schema.copy_table_columns_from(schema); } - if (!ObjectStore::needs_migration(required_changes)) - return false; + else { + // The structure of the schema has changed, so replace our copy of the schema. + // FIXME: This invalidates any pointers to the object schemas within the schema vector, + // which will cause problems for anyone that caches such a pointer. + m_schema = std::move(schema); + } + } + else { + ObjectStore::verify_valid_external_changes(m_schema.compare(schema)); + m_schema.copy_table_columns_from(schema); } + notify_schema_changed(); +} +bool Realm::reset_file(Schema& schema, std::vector& required_changes) +{ // FIXME: this does not work if multiple processes try to open the file at // the same time, or even multiple threads if there is not any external // synchronization. The latter is probably fixable, but making it @@ -295,130 +323,276 @@ bool Realm::reset_file_if_needed(Schema& schema, uint64_t version, std::vectorclear_schema_cache_and_set_schema_version(m_schema_version); return false; } -void Realm::update_schema(Schema schema, uint64_t version, MigrationFunction migration_function, bool in_transaction) +bool Realm::schema_change_needs_write_transaction(Schema& schema, + std::vector& changes, + uint64_t version) { - schema.validate(); - read_schema_from_group_if_needed(); - std::vector required_changes = m_schema.compare(schema); + if (version == m_schema_version && changes.empty()) + return false; - auto no_changes_required = [&] { - switch (m_config.schema_mode) { - case SchemaMode::Automatic: - if (version < m_schema_version && m_schema_version != ObjectStore::NotVersioned) { - throw InvalidSchemaVersionException(m_schema_version, version); - } - if (version == m_schema_version) { - if (required_changes.empty()) { - set_schema(std::move(schema), version); - return true; - } - ObjectStore::verify_no_migration_required(required_changes); - } - return false; + switch (m_config.schema_mode) { + case SchemaMode::Automatic: + if (version < m_schema_version && m_schema_version != ObjectStore::NotVersioned) + throw InvalidSchemaVersionException(m_schema_version, version); + return true; + + case SchemaMode::Immutable: + if (version != m_schema_version) + throw InvalidSchemaVersionException(m_schema_version, version); + REALM_FALLTHROUGH; + case SchemaMode::ReadOnlyAlternative: + ObjectStore::verify_compatible_for_immutable_and_readonly(changes); + return false; + + case SchemaMode::ResetFile: + if (m_schema_version == ObjectStore::NotVersioned) + return true; + if (m_schema_version == version && !ObjectStore::needs_migration(changes)) + return true; + reset_file(schema, changes); + return true; - case SchemaMode::ReadOnly: - if (version != m_schema_version) - throw InvalidSchemaVersionException(m_schema_version, version); - ObjectStore::verify_no_migration_required(m_schema.compare(schema)); - set_schema(std::move(schema), version); + case SchemaMode::Additive: { + bool will_apply_index_changes = version > m_schema_version; + if (ObjectStore::verify_valid_additive_changes(changes, will_apply_index_changes)) return true; + return version != m_schema_version; + } - case SchemaMode::ResetFile: - return reset_file_if_needed(schema, version, required_changes); + case SchemaMode::Manual: + if (version < m_schema_version && m_schema_version != ObjectStore::NotVersioned) + throw InvalidSchemaVersionException(m_schema_version, version); + if (version == m_schema_version) { + ObjectStore::verify_no_changes_required(changes); + REALM_UNREACHABLE(); // changes is non-empty so above line always throws + } + return true; + } + REALM_COMPILER_HINT_UNREACHABLE(); +} - case SchemaMode::Additive: - if (required_changes.empty()) { - set_schema(std::move(schema), version); - return version == m_schema_version; - } - ObjectStore::verify_valid_additive_changes(required_changes); - return false; +Schema Realm::get_full_schema() +{ + if (!m_read_only_group) + refresh(); - case SchemaMode::Manual: - if (version < m_schema_version && m_schema_version != ObjectStore::NotVersioned) { - throw InvalidSchemaVersionException(m_schema_version, version); - } - if (version == m_schema_version) { - ObjectStore::verify_no_changes_required(required_changes); - return true; - } - return false; - } - REALM_COMPILER_HINT_UNREACHABLE(); - }; + // If the user hasn't specified a schema previously then m_schema is always + // the full schema + if (m_dynamic_schema) + return m_schema; + + // Otherwise we may have a subset of the file's schema, so we need to get + // the complete thing to calculate what changes to make + if (m_read_only_group) + return ObjectStore::schema_from_group(read_group()); + + Schema actual_schema; + uint64_t actual_version; + uint64_t transaction = -1; + bool got_cached = m_coordinator->get_cached_schema(actual_schema, actual_version, transaction); + if (!got_cached || transaction != m_shared_group->get_version_of_current_transaction().version) + return ObjectStore::schema_from_group(read_group()); + return actual_schema; +} + +void Realm::set_schema_subset(Schema schema) +{ + REALM_ASSERT(m_dynamic_schema); + REALM_ASSERT(m_schema_version != ObjectStore::NotVersioned); + + std::vector changes = m_schema.compare(schema); + switch (m_config.schema_mode) { + case SchemaMode::Automatic: + case SchemaMode::ResetFile: + ObjectStore::verify_no_migration_required(changes); + break; + + case SchemaMode::Immutable: + case SchemaMode::ReadOnlyAlternative: + ObjectStore::verify_compatible_for_immutable_and_readonly(changes); + break; + + case SchemaMode::Additive: + ObjectStore::verify_valid_additive_changes(changes); + break; + + case SchemaMode::Manual: + ObjectStore::verify_no_changes_required(changes); + break; + } + + set_schema(m_schema, std::move(schema)); +} - if (no_changes_required()) +void Realm::update_schema(Schema schema, uint64_t version, MigrationFunction migration_function, + DataInitializationFunction initialization_function, bool in_transaction) +{ + schema.validate(); + + Schema actual_schema = get_full_schema(); + std::vector required_changes = actual_schema.compare(schema); + + if (!schema_change_needs_write_transaction(schema, required_changes, version)) { + set_schema(actual_schema, std::move(schema)); return; + } // Either the schema version has changed or we need to do non-migration changes - m_group->set_schema_change_notification_handler(nullptr); if (!in_transaction) { transaction::begin_without_validation(*m_shared_group); + + // Beginning the write transaction may have advanced the version and left + // us with nothing to do if someone else initialized the schema on disk + if (m_new_schema) { + actual_schema = *m_new_schema; + required_changes = actual_schema.compare(schema); + if (!schema_change_needs_write_transaction(schema, required_changes, version)) { + cancel_transaction(); + cache_new_schema(); + set_schema(actual_schema, std::move(schema)); + return; + } + } + cache_new_schema(); } - add_schema_change_handler(); // Cancel the write transaction if we exit this function before committing it - struct WriteTransactionGuard { - Realm& realm; - bool& in_transaction; + auto cleanup = util::make_scope_exit([&]() noexcept { // When in_transaction is true, caller is responsible to cancel the transaction. - ~WriteTransactionGuard() { if (!in_transaction && realm.is_in_transaction()) realm.cancel_transaction(); } - } write_transaction_guard{*this, in_transaction}; - - // If beginning the write transaction advanced the version, then someone else - // may have updated the schema and we need to re-read it - // We can't just begin the write transaction before checking anything because - // that means that write transactions would block opening Realms in other processes - if (read_schema_from_group_if_needed()) { - required_changes = m_schema.compare(schema); - if (no_changes_required()) - return; - } + if (!in_transaction && is_in_transaction()) + cancel_transaction(); + }); + uint64_t old_schema_version = m_schema_version; bool additive = m_config.schema_mode == SchemaMode::Additive; if (migration_function && !additive) { auto wrapper = [&] { SharedRealm old_realm(new Realm(m_config, nullptr)); // Need to open in read-write mode so that it uses a SharedGroup, but // users shouldn't actually be able to write via the old realm - old_realm->m_config.schema_mode = SchemaMode::ReadOnly; - + old_realm->m_config.schema_mode = SchemaMode::Immutable; migration_function(old_realm, shared_from_this(), m_schema); }; - ObjectStore::apply_schema_changes(read_group(), m_schema, m_schema_version, - schema, version, m_config.schema_mode, required_changes, wrapper); + + // migration function needs to see the target schema on the "new" Realm + std::swap(m_schema, schema); + std::swap(m_schema_version, version); + m_in_migration = true; + auto restore = util::make_scope_exit([&]() noexcept { + std::swap(m_schema, schema); + std::swap(m_schema_version, version); + m_in_migration = false; + }); + + ObjectStore::apply_schema_changes(read_group(), version, m_schema, m_schema_version, + m_config.schema_mode, required_changes, util::none, wrapper); } else { - ObjectStore::apply_schema_changes(read_group(), m_schema, m_schema_version, - schema, version, m_config.schema_mode, required_changes); + util::Optional sync_user_id; +#if REALM_ENABLE_SYNC + if (m_config.sync_config && m_config.sync_config->is_partial) + sync_user_id = m_config.sync_config->user->identity(); +#endif + ObjectStore::apply_schema_changes(read_group(), m_schema_version, schema, version, + m_config.schema_mode, required_changes, std::move(sync_user_id)); REALM_ASSERT_DEBUG(additive || (required_changes = ObjectStore::schema_from_group(read_group()).compare(schema)).empty()); } + if (initialization_function && old_schema_version == ObjectStore::NotVersioned) { + // Initialization function needs to see the latest schema + uint64_t temp_version = ObjectStore::get_schema_version(read_group()); + std::swap(m_schema, schema); + std::swap(m_schema_version, temp_version); + auto restore = util::make_scope_exit([&]() noexcept { + std::swap(m_schema, schema); + std::swap(m_schema_version, temp_version); + }); + initialization_function(shared_from_this()); + } + if (!in_transaction) { commit_transaction(); } - m_coordinator->update_schema(m_schema, version); + + m_schema = std::move(schema); + m_schema_version = ObjectStore::get_schema_version(read_group()); + m_dynamic_schema = false; + m_coordinator->clear_schema_cache_and_set_schema_version(version); + notify_schema_changed(); } void Realm::add_schema_change_handler() { - if (m_coordinator && m_config.schema_mode == SchemaMode::Additive) { - m_group->set_schema_change_notification_handler([&] { - auto new_schema = ObjectStore::schema_from_group(read_group()); - auto required_changes = m_schema.compare(new_schema); - ObjectStore::verify_valid_additive_changes(required_changes); - m_schema.copy_table_columns_from(new_schema); - m_coordinator->update_schema(m_schema, m_schema_version); - }); + if (m_config.immutable()) + return; + m_group->set_schema_change_notification_handler([&] { + m_new_schema = ObjectStore::schema_from_group(read_group()); + m_schema_version = ObjectStore::get_schema_version(read_group()); + if (m_dynamic_schema) { + // FIXME: This invalidates any pointers to the object schemas within the schema vector, + // which will cause problems for anyone that caches such a pointer. + m_schema = *m_new_schema; + } + else + m_schema.copy_table_columns_from(*m_new_schema); + + notify_schema_changed(); + }); +} + +void Realm::cache_new_schema() +{ + if (!m_shared_group) + return; + + auto new_version = m_shared_group->get_version_of_current_transaction().version; + if (m_coordinator) { + if (m_new_schema) + m_coordinator->cache_schema(std::move(*m_new_schema), m_schema_version, new_version); + else + m_coordinator->advance_schema_cache(m_schema_transaction_version, new_version); + } + m_schema_transaction_version = new_version; + m_new_schema = util::none; +} + +void Realm::translate_schema_error() +{ + // Open another copy of the file to read the new (incompatible) schema without changing + // our read transaction + auto config = m_config; + config.schema = util::none; + auto realm = Realm::make_shared_realm(std::move(config), nullptr); + auto& new_schema = realm->schema(); + + // Should always throw + ObjectStore::verify_valid_external_changes(m_schema.compare(new_schema, true)); + + // Something strange happened so just rethrow the old exception + throw; +} + +void Realm::notify_schema_changed() +{ + if (m_binding_context) { + m_binding_context->schema_did_change(m_schema); } } static void check_read_write(Realm *realm) { - if (realm->config().read_only()) { + if (realm->config().immutable()) { + throw InvalidTransactionException("Can't perform transactions on read-only Realms."); + } +} + +static void check_write(Realm* realm) +{ + if (realm->config().immutable() || realm->config().read_only_alternative()) { throw InvalidTransactionException("Can't perform transactions on read-only Realms."); } } @@ -457,20 +631,24 @@ bool Realm::is_in_transaction() const noexcept void Realm::begin_transaction() { - check_read_write(this); + check_write(this); verify_thread(); if (is_in_transaction()) { throw InvalidTransactionException("The Realm is already in a write transaction"); } + // Any of the callbacks to user code below could drop the last remaining + // strong reference to `this` + auto retain_self = shared_from_this(); + // If we're already in the middle of sending notifications, just begin the // write transaction without sending more notifications. If this actually // advances the read version this could leave the user in an inconsistent // state, but that's unavoidable. if (m_is_sending_notifications) { _impl::NotifierPackage notifiers; - transaction::begin(*m_shared_group, m_binding_context.get(), notifiers); + transaction::begin(m_shared_group, m_binding_context.get(), notifiers); return; } @@ -480,12 +658,18 @@ void Realm::begin_transaction() m_is_sending_notifications = true; auto cleanup = util::make_scope_exit([this]() noexcept { m_is_sending_notifications = false; }); - m_coordinator->promote_to_write(*this); + try { + m_coordinator->promote_to_write(*this); + } + catch (_impl::UnsupportedSchemaChange const&) { + translate_schema_error(); + } + cache_new_schema(); } void Realm::commit_transaction() { - check_read_write(this); + check_write(this); verify_thread(); if (!is_in_transaction()) { @@ -493,11 +677,13 @@ void Realm::commit_transaction() } m_coordinator->commit_write(*this); + cache_new_schema(); + invalidate_permission_cache(); } void Realm::cancel_transaction() { - check_read_write(this); + check_write(this); verify_thread(); if (!is_in_transaction()) { @@ -505,6 +691,7 @@ void Realm::cancel_transaction() } transaction::cancel(*m_shared_group, m_binding_context.get()); + invalidate_permission_cache(); } void Realm::invalidate() @@ -524,6 +711,7 @@ void Realm::invalidate() return; } + m_permissions_cache = nullptr; m_shared_group->end_read(); m_group = nullptr; } @@ -532,18 +720,18 @@ bool Realm::compact() { verify_thread(); - if (m_config.read_only()) { + if (m_config.immutable() || m_config.read_only_alternative()) { throw InvalidTransactionException("Can't compact a read-only Realm"); } if (is_in_transaction()) { throw InvalidTransactionException("Can't compact a Realm within a write transaction"); } - Group& group = read_group(); - for (auto &object_schema : m_schema) { - ObjectStore::table_for_object_type(group, object_schema.name)->optimize(); + verify_open(); + // FIXME: when enum columns are ready, optimise all tables in a write transaction + if (m_group) { + m_shared_group->end_read(); } - m_shared_group->end_read(); m_group = nullptr; return m_shared_group->compact(); @@ -580,6 +768,11 @@ void Realm::notify() } verify_thread(); + invalidate_permission_cache(); + + // Any of the callbacks to user code below could drop the last remaining + // strong reference to `this` + auto retain_self = shared_from_this(); if (m_binding_context) { m_binding_context->before_notify(); @@ -604,13 +797,21 @@ void Realm::notify() m_is_sending_notifications = true; if (m_auto_refresh) { if (m_group) { - m_coordinator->advance_to_ready(*this); + try { + m_coordinator->advance_to_ready(*this); + } + catch (_impl::UnsupportedSchemaChange const&) { + translate_schema_error(); + } + cache_new_schema(); } else { if (m_binding_context) { m_binding_context->did_change({}, {}); } - m_coordinator->process_available_async(*this); + if (!is_closed()) { + m_coordinator->process_available_async(*this); + } } } } @@ -629,6 +830,11 @@ bool Realm::refresh() if (m_is_sending_notifications) { return false; } + invalidate_permission_cache(); + + // Any of the callbacks to user code below could drop the last remaining + // strong reference to `this` + auto retain_self = shared_from_this(); m_is_sending_notifications = true; auto cleanup = util::make_scope_exit([this]() noexcept { m_is_sending_notifications = false; }); @@ -637,7 +843,14 @@ bool Realm::refresh() m_binding_context->before_notify(); } if (m_group) { - return m_coordinator->advance_to_latest(*this); + try { + bool version_changed = m_coordinator->advance_to_latest(*this); + cache_new_schema(); + return version_changed; + } + catch (_impl::UnsupportedSchemaChange const&) { + translate_schema_error(); + } } // No current read transaction, so just create a new one @@ -648,7 +861,7 @@ bool Realm::refresh() bool Realm::can_deliver_notifications() const noexcept { - if (m_config.read_only()) { + if (m_config.immutable()) { return false; } @@ -675,6 +888,7 @@ void Realm::close() m_coordinator->unregister_realm(this); } + m_permissions_cache = nullptr; m_group = nullptr; m_shared_group = nullptr; m_history = nullptr; @@ -719,17 +933,21 @@ T Realm::resolve_thread_safe_reference(ThreadSafeReference reference) throw MismatchedRealmException("Cannot resolve thread safe reference in Realm with different configuration " "than the source Realm."); } + invalidate_permission_cache(); + + // Any of the callbacks to user code below could drop the last remaining + // strong reference to `this` + auto retain_self = shared_from_this(); // Ensure we're on the same version as the reference if (!m_group) { // A read transaction doesn't yet exist, so create at the reference's version - m_group = &const_cast(m_shared_group->begin_read(reference.m_version_id)); - add_schema_change_handler(); + begin_read(reference.m_version_id); } else { // A read transaction does exist, but let's make sure that its version matches the reference's auto current_version = m_shared_group->get_version_of_current_transaction(); - SharedGroup::VersionID reference_version = SharedGroup::VersionID(reference.m_version_id); + VersionID reference_version(reference.m_version_id); if (reference_version == current_version) { return std::move(reference).import_into_realm(shared_from_this()); @@ -743,18 +961,17 @@ T Realm::resolve_thread_safe_reference(ThreadSafeReference reference) if (reference_version < current_version) { // Duplicate config for uncached Realm so we don't advance the user's Realm Realm::Config config = m_coordinator->get_config(); + config.automatic_change_notifications = false; config.cache = false; + config.schema = util::none; SharedRealm temporary_realm = m_coordinator->get_realm(config); - REALM_ASSERT(!temporary_realm->is_in_read_transaction()); - - // Begin read in temporary Realm at reference's version - temporary_realm->m_group = - &const_cast(temporary_realm->m_shared_group->begin_read(reference_version)); + temporary_realm->begin_read(reference_version); // With reference imported, advance temporary Realm to our version T imported_value = std::move(reference).import_into_realm(temporary_realm); - transaction::advance(*temporary_realm->m_shared_group, temporary_realm->m_binding_context.get(), - current_version); + transaction::advance(*temporary_realm->m_shared_group, nullptr, current_version); + if (!imported_value.is_valid()) + return T{}; reference = ThreadSafeReference(imported_value); } } @@ -766,6 +983,98 @@ template Object Realm::resolve_thread_safe_reference(ThreadSafeReference template List Realm::resolve_thread_safe_reference(ThreadSafeReference reference); template Results Realm::resolve_thread_safe_reference(ThreadSafeReference reference); +#if REALM_ENABLE_SYNC +static_assert(static_cast(ComputedPrivileges::Read) == static_cast(sync::Privilege::Read), ""); +static_assert(static_cast(ComputedPrivileges::Update) == static_cast(sync::Privilege::Update), ""); +static_assert(static_cast(ComputedPrivileges::Delete) == static_cast(sync::Privilege::Delete), ""); +static_assert(static_cast(ComputedPrivileges::SetPermissions) == static_cast(sync::Privilege::SetPermissions), ""); +static_assert(static_cast(ComputedPrivileges::Query) == static_cast(sync::Privilege::Query), ""); +static_assert(static_cast(ComputedPrivileges::Create) == static_cast(sync::Privilege::Create), ""); +static_assert(static_cast(ComputedPrivileges::ModifySchema) == static_cast(sync::Privilege::ModifySchema), ""); + +static constexpr const uint8_t s_allRealmPrivileges = sync::Privilege::Read + | sync::Privilege::Update + | sync::Privilege::SetPermissions + | sync::Privilege::ModifySchema; +static constexpr const uint8_t s_allClassPrivileges = sync::Privilege::Read + | sync::Privilege::Update + | sync::Privilege::Create + | sync::Privilege::Query + | sync::Privilege::SetPermissions; +static constexpr const uint8_t s_allObjectPrivileges = sync::Privilege::Read + | sync::Privilege::Update + | sync::Privilege::Delete + | sync::Privilege::SetPermissions; + +bool Realm::init_permission_cache() +{ + verify_thread(); + + if (m_permissions_cache) { + // Rather than trying to track changes to permissions tables, just skip the caching + // entirely within write transactions for now + if (is_in_transaction()) + m_permissions_cache->clear(); + return true; + } + + // Admin users bypass permissions checks outside of the logic in PermissionsCache + if (m_config.sync_config && m_config.sync_config->is_partial && !m_config.sync_config->user->is_admin()) { + m_permissions_cache = std::make_unique(read_group(), m_config.sync_config->user->identity()); + return true; + } + return false; +} + +void Realm::invalidate_permission_cache() +{ + if (m_permissions_cache) + m_permissions_cache->clear(); +} + +ComputedPrivileges Realm::get_privileges() +{ + if (!init_permission_cache()) + return static_cast(s_allRealmPrivileges); + return static_cast(m_permissions_cache->get_realm_privileges() & s_allRealmPrivileges); +} + +static uint8_t inherited_mask(uint32_t privileges) +{ + uint8_t mask = ~0; + if (!(privileges & sync::Privilege::Read)) + mask = 0; + else if (!(privileges & sync::Privilege::Update)) + mask = static_cast(sync::Privilege::Read | sync::Privilege::Query); + return mask; +} + +ComputedPrivileges Realm::get_privileges(StringData object_type) +{ + if (!init_permission_cache()) + return static_cast(s_allClassPrivileges); + auto privileges = inherited_mask(m_permissions_cache->get_realm_privileges()) + & m_permissions_cache->get_class_privileges(object_type); + return static_cast(privileges & s_allClassPrivileges); +} + +ComputedPrivileges Realm::get_privileges(RowExpr row) +{ + if (!init_permission_cache()) + return static_cast(s_allObjectPrivileges); + + auto& table = *row.get_table(); + auto object_type = ObjectStore::object_type_for_table_name(table.get_name()); + sync::GlobalID global_id{object_type, sync::object_id_for_row(read_group(), table, row.get_index())}; + auto privileges = inherited_mask(m_permissions_cache->get_realm_privileges()) + & inherited_mask(m_permissions_cache->get_class_privileges(object_type)) + & m_permissions_cache->get_object_privileges(global_id); + return static_cast(privileges & s_allObjectPrivileges); +} +#else +void Realm::invalidate_permission_cache() { } +#endif + MismatchedConfigException::MismatchedConfigException(StringData message, StringData path) : std::logic_error(util::format(message.data(), path)) { } @@ -778,15 +1087,18 @@ SharedGroup& RealmFriend::get_shared_group(Realm& realm) return *realm.m_shared_group; } -Group& RealmFriend::read_group_to(Realm& realm, VersionID& version) +Group& RealmFriend::read_group_to(Realm& realm, VersionID version) { - if (!realm.m_group) { - realm.m_group = &const_cast(realm.m_shared_group->begin_read(version)); - realm.add_schema_change_handler(); - } - else if (version != realm.m_shared_group->get_version_of_current_transaction()) { + if (realm.m_group && realm.m_shared_group->get_version_of_current_transaction() == version) + return *realm.m_group; + + if (realm.m_group) realm.m_shared_group->end_read(); - realm.m_group = &const_cast(realm.m_shared_group->begin_read(version)); - } + realm.begin_read(version); return *realm.m_group; } + +std::size_t Realm::compute_size() { + Group& group = read_group(); + return group.compute_aggregated_byte_size(); +} diff --git a/Pods/Realm/Realm/ObjectStore/src/sync/impl/apple/network_reachability_observer.cpp b/Pods/Realm/Realm/ObjectStore/src/sync/impl/apple/network_reachability_observer.cpp new file mode 100644 index 0000000..ad040a4 --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/sync/impl/apple/network_reachability_observer.cpp @@ -0,0 +1,127 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "sync/impl/apple/network_reachability_observer.hpp" + +#if NETWORK_REACHABILITY_AVAILABLE + +using namespace realm; +using namespace realm::_impl; + +namespace { + +NetworkReachabilityStatus reachability_status_for_flags(SCNetworkReachabilityFlags flags) +{ + if (!(flags & kSCNetworkReachabilityFlagsReachable)) + return NotReachable; + + if (flags & kSCNetworkReachabilityFlagsConnectionRequired) { + if (!(flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) || + (flags & kSCNetworkReachabilityFlagsInterventionRequired)) + return NotReachable; + } + + NetworkReachabilityStatus status = ReachableViaWiFi; + +#if TARGET_OS_IPHONE + if (flags & kSCNetworkReachabilityFlagsIsWWAN) + status = ReachableViaWWAN; +#endif + + return status; +} + +} // (anonymous namespace) + +NetworkReachabilityObserver::NetworkReachabilityObserver(util::Optional hostname, + std::function handler) +: m_callback_queue(dispatch_queue_create("io.realm.sync.reachability", DISPATCH_QUEUE_SERIAL)) +, m_change_handler(std::move(handler)) +{ + if (hostname) { + m_reachability_ref = util::adoptCF(SystemConfiguration::shared().network_reachability_create_with_name(nullptr, + hostname->c_str())); + } else { + struct sockaddr zeroAddress = {}; + zeroAddress.sa_len = sizeof(zeroAddress); + zeroAddress.sa_family = AF_INET; + + m_reachability_ref = util::adoptCF(SystemConfiguration::shared().network_reachability_create_with_address(nullptr, + &zeroAddress)); + } +} + +NetworkReachabilityObserver::~NetworkReachabilityObserver() +{ + stop_observing(); + dispatch_release(m_callback_queue); +} + +NetworkReachabilityStatus NetworkReachabilityObserver::reachability_status() const +{ + SCNetworkReachabilityFlags flags; + + if (SystemConfiguration::shared().network_reachability_get_flags(m_reachability_ref.get(), &flags)) + return reachability_status_for_flags(flags); + + return NotReachable; +} + +bool NetworkReachabilityObserver::start_observing() +{ + m_previous_status = reachability_status(); + + auto callback = [](SCNetworkReachabilityRef, SCNetworkReachabilityFlags, void* self) { + static_cast(self)->reachability_changed(); + }; + + SCNetworkReachabilityContext context = {0, this, nullptr, nullptr, nullptr}; + + if (!SystemConfiguration::shared().network_reachability_set_callback(m_reachability_ref.get(), callback, &context)) + return false; + + if (!SystemConfiguration::shared().network_reachability_set_dispatch_queue(m_reachability_ref.get(), m_callback_queue)) + return false; + + return true; +} + +void NetworkReachabilityObserver::stop_observing() +{ + SystemConfiguration::shared().network_reachability_set_dispatch_queue(m_reachability_ref.get(), nullptr); + SystemConfiguration::shared().network_reachability_set_callback(m_reachability_ref.get(), nullptr, nullptr); + + // Wait for all previously-enqueued blocks to execute to guarantee that + // no callback will be called after returning from this method + dispatch_sync(m_callback_queue, ^{}); +} + +void NetworkReachabilityObserver::reachability_changed() +{ + auto current_status = reachability_status(); + + // When observing reachability of the specific host the callback might be called + // several times (because of DNS queries) with the same reachability flags while + // the caller should be notified only when the reachability status is really changed. + if (current_status != m_previous_status) { + m_change_handler(current_status); + m_previous_status = current_status; + } +} + +#endif diff --git a/Pods/Realm/Realm/ObjectStore/src/sync/impl/apple/system_configuration.cpp b/Pods/Realm/Realm/ObjectStore/src/sync/impl/apple/system_configuration.cpp new file mode 100644 index 0000000..6c4c127 --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/sync/impl/apple/system_configuration.cpp @@ -0,0 +1,98 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2017 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "sync/impl/apple/system_configuration.hpp" + +#if NETWORK_REACHABILITY_AVAILABLE + +#include +#include "dlfcn.h" + +using namespace realm; +using namespace realm::_impl; + +SystemConfiguration::SystemConfiguration() +{ + m_framework_handle = dlopen("/System/Library/Frameworks/SystemConfiguration.framework/SystemConfiguration", RTLD_LAZY); + + if (m_framework_handle) { + m_create_with_name = (create_with_name_t)dlsym(m_framework_handle, "SCNetworkReachabilityCreateWithName"); + m_create_with_address = (create_with_address_t)dlsym(m_framework_handle, "SCNetworkReachabilityCreateWithAddress"); + m_set_dispatch_queue = (set_dispatch_queue_t)dlsym(m_framework_handle, "SCNetworkReachabilitySetDispatchQueue"); + m_set_callback = (set_callback_t)dlsym(m_framework_handle, "SCNetworkReachabilitySetCallback"); + m_get_flags = (get_flags_t)dlsym(m_framework_handle, "SCNetworkReachabilityGetFlags"); + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + asl_log(nullptr, nullptr, ASL_LEVEL_WARNING, "network reachability is not available"); +#pragma clang diagnostic pop + } +} + +SystemConfiguration& SystemConfiguration::shared() +{ + static SystemConfiguration system_configuration; + + return system_configuration; +} + +SCNetworkReachabilityRef SystemConfiguration::network_reachability_create_with_name(CFAllocatorRef allocator, + const char *hostname) +{ + if (m_create_with_name) + return m_create_with_name(allocator, hostname); + + return nullptr; +} + +SCNetworkReachabilityRef SystemConfiguration::network_reachability_create_with_address(CFAllocatorRef allocator, + const sockaddr *address) +{ + if (m_create_with_address) + return m_create_with_address(allocator, address); + + return nullptr; +} + +bool SystemConfiguration::network_reachability_set_dispatch_queue(SCNetworkReachabilityRef target, dispatch_queue_t queue) +{ + if (m_set_dispatch_queue) + return m_set_dispatch_queue(target, queue); + + return false; +} + +bool SystemConfiguration::network_reachability_set_callback(SCNetworkReachabilityRef target, + SCNetworkReachabilityCallBack callback, + SCNetworkReachabilityContext *context) +{ + if (m_set_callback) + return m_set_callback(target, callback, context); + + return false; +} + +bool SystemConfiguration::network_reachability_get_flags(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags *flags) +{ + if (m_get_flags) + return m_get_flags(target, flags); + + return false; +} + +#endif // NETWORK_REACHABILITY_AVAILABLE diff --git a/Pods/Realm/Realm/ObjectStore/src/sync/impl/sync_file.cpp b/Pods/Realm/Realm/ObjectStore/src/sync/impl/sync_file.cpp index 9c5027a..10ca220 100644 --- a/Pods/Realm/Realm/ObjectStore/src/sync/impl/sync_file.cpp +++ b/Pods/Realm/Realm/ObjectStore/src/sync/impl/sync_file.cpp @@ -18,16 +18,16 @@ #include "sync/impl/sync_file.hpp" -#include "util/time.hpp" - #include +#include #include #include #include #include +#include -#if WIN32 +#ifdef _WIN32 #include #include @@ -82,41 +82,12 @@ char decoded_char_for(const std::string& percent_encoding, size_t index) namespace util { -void remove_nonempty_dir(const std::string& path) -{ - // Open the directory and list all the files. - DIR *dir_listing = opendir(path.c_str()); - if (!dir_listing) { - return; - } - auto cleanup = util::make_scope_exit([=]() noexcept { closedir(dir_listing); }); - while (struct dirent *file = readdir(dir_listing)) { - auto file_type = file->d_type; - std::string file_name = file->d_name; - if (file_name == "." || file_name == "..") { - continue; - } - if (file_type == DT_REG || file_type == DT_FIFO) { - File::try_remove(file_path_by_appending_component(path, file_name)); - } else if (file_type == DT_DIR) { - // Directory, recurse - remove_nonempty_dir(file_path_by_appending_component(path, file_name, FilePathType::Directory)); - } - } - // Delete the directory itself - try { - util::remove_dir(path); - } - catch (File::NotFound const&) { - } -} - std::string make_percent_encoded_string(const std::string& raw_string) { std::string buffer; buffer.reserve(raw_string.size()); for (size_t i=0; i user_info) const { - REALM_ASSERT(user_identity.length() > 0); - if (filename_is_reserved(user_identity)) { + REALM_ASSERT(local_identity.length() > 0); + std::string escaped = util::make_percent_encoded_string(local_identity); + if (filename_is_reserved(escaped)) throw std::invalid_argument("A user can't have an identifier reserved by the filesystem."); - } + auto user_path = file_path_by_appending_component(get_base_sync_directory(), - user_identity, + escaped, util::FilePathType::Directory); - util::try_make_dir(user_path); + bool dir_created = util::try_make_dir(user_path); + if (dir_created && user_info) { + // Add a text file in the user directory containing the user identity, for backup purposes. + // Only do this the first time the directory is created. + auto info_path = util::file_path_by_appending_component(user_path, c_user_info_file); + std::ofstream info_file; + info_file.open(info_path.c_str()); + if (info_file.is_open()) { + info_file << user_info->user_id << "\n" << user_info->auth_server_url << "\n"; + info_file.close(); + } + } return user_path; } -void SyncFileManager::remove_user_directory(const std::string& user_identity) const +void SyncFileManager::remove_user_directory(const std::string& local_identity) const { - REALM_ASSERT(user_identity.length() > 0); - if (filename_is_reserved(user_identity)) { + REALM_ASSERT(local_identity.length() > 0); + const auto& escaped = util::make_percent_encoded_string(local_identity); + if (filename_is_reserved(escaped)) throw std::invalid_argument("A user can't have an identifier reserved by the filesystem."); - } + auto user_path = file_path_by_appending_component(get_base_sync_directory(), - user_identity, + escaped, util::FilePathType::Directory); - util::remove_nonempty_dir(user_path); + util::try_remove_dir_recursive(user_path); +} + +bool SyncFileManager::try_rename_user_directory(const std::string& old_name, const std::string& new_name) const +{ + REALM_ASSERT_DEBUG(old_name.length() > 0 && new_name.length() > 0); + const auto& old_name_escaped = util::make_percent_encoded_string(old_name); + const auto& new_name_escaped = util::make_percent_encoded_string(new_name); + const std::string& base = get_base_sync_directory(); + if (filename_is_reserved(old_name_escaped) || filename_is_reserved(new_name_escaped)) + throw std::invalid_argument("A user directory can't be renamed using a reserved identifier."); + + const auto& old_path = file_path_by_appending_component(base, old_name_escaped, util::FilePathType::Directory); + const auto& new_path = file_path_by_appending_component(base, new_name_escaped, util::FilePathType::Directory); + + try { + File::move(old_path, new_path); + } catch (File::NotFound const&) { + return false; + } + return true; } bool SyncFileManager::remove_realm(const std::string& absolute_path) const @@ -285,9 +296,7 @@ bool SyncFileManager::remove_realm(const std::string& absolute_path) const // Remove the management directory (e.g. "example.realm.management"). auto management_path = util::file_path_by_appending_extension(absolute_path, "management"); try { - util::remove_nonempty_dir(management_path); - } - catch (File::NotFound const&) { + util::try_remove_dir_recursive(management_path); } catch (File::AccessError const&) { success = false; @@ -303,7 +312,7 @@ bool SyncFileManager::copy_realm_file(const std::string& old_path, const std::st return false; } File::copy(old_path, new_path); - } + } catch (File::NotFound const&) { return false; } @@ -313,27 +322,28 @@ bool SyncFileManager::copy_realm_file(const std::string& old_path, const std::st return true; } -bool SyncFileManager::remove_realm(const std::string& user_identity, const std::string& raw_realm_path) const +bool SyncFileManager::remove_realm(const std::string& local_identity, const std::string& raw_realm_path) const { - REALM_ASSERT(user_identity.length() > 0); + REALM_ASSERT(local_identity.length() > 0); REALM_ASSERT(raw_realm_path.length() > 0); - if (filename_is_reserved(user_identity) || filename_is_reserved(raw_realm_path)) { + if (filename_is_reserved(local_identity) || filename_is_reserved(raw_realm_path)) throw std::invalid_argument("A user or Realm can't have an identifier reserved by the filesystem."); - } + auto escaped = util::make_percent_encoded_string(raw_realm_path); - auto realm_path = util::file_path_by_appending_component(user_directory(user_identity), escaped); + auto realm_path = util::file_path_by_appending_component(user_directory(local_identity), escaped); return remove_realm(realm_path); } -std::string SyncFileManager::path(const std::string& user_identity, const std::string& raw_realm_path) const +std::string SyncFileManager::path(const std::string& local_identity, const std::string& raw_realm_path, + util::Optional user_info) const { - REALM_ASSERT(user_identity.length() > 0); + REALM_ASSERT(local_identity.length() > 0); REALM_ASSERT(raw_realm_path.length() > 0); - if (filename_is_reserved(user_identity) || filename_is_reserved(raw_realm_path)) { + if (filename_is_reserved(local_identity) || filename_is_reserved(raw_realm_path)) throw std::invalid_argument("A user or Realm can't have an identifier reserved by the filesystem."); - } + auto escaped = util::make_percent_encoded_string(raw_realm_path); - auto realm_path = util::file_path_by_appending_component(user_directory(user_identity), escaped); + auto realm_path = util::file_path_by_appending_component(user_directory(local_identity, user_info), escaped); return realm_path; } @@ -352,7 +362,7 @@ bool SyncFileManager::remove_metadata_realm() const c_metadata_directory, util::FilePathType::Directory); try { - util::remove_nonempty_dir(dir_path); + util::try_remove_dir_recursive(dir_path); return true; } catch (File::AccessError const&) { diff --git a/Pods/Realm/Realm/ObjectStore/src/sync/impl/sync_metadata.cpp b/Pods/Realm/Realm/ObjectStore/src/sync/impl/sync_metadata.cpp index 9a82cc5..5ac9134 100644 --- a/Pods/Realm/Realm/ObjectStore/src/sync/impl/sync_metadata.cpp +++ b/Pods/Realm/Realm/ObjectStore/src/sync/impl/sync_metadata.cpp @@ -23,6 +23,7 @@ #include "property.hpp" #include "results.hpp" #include "schema.hpp" +#include "util/uuid.hpp" #if REALM_PLATFORM_APPLE #include "impl/apple/keychain_helper.hpp" #endif @@ -30,13 +31,14 @@ #include #include -namespace realm { - +namespace { static const char * const c_sync_userMetadata = "UserMetadata"; static const char * const c_sync_marked_for_removal = "marked_for_removal"; static const char * const c_sync_identity = "identity"; +static const char * const c_sync_local_uuid = "local_uuid"; static const char * const c_sync_auth_server_url = "auth_server_url"; static const char * const c_sync_user_token = "user_token"; +static const char * const c_sync_user_is_admin = "user_is_admin"; static const char * const c_sync_fileActionMetadata = "FileActionMetadata"; static const char * const c_sync_original_name = "original_name"; @@ -44,48 +46,54 @@ static const char * const c_sync_new_name = "new_name"; static const char * const c_sync_action = "action"; static const char * const c_sync_url = "url"; +static const char * const c_sync_clientMetadata = "ClientMetadata"; +static const char * const c_sync_uuid = "uuid"; -SyncMetadataManager::SyncMetadataManager(std::string path, - bool should_encrypt, - util::Optional> encryption_key) +realm::Schema make_schema() { - std::lock_guard lock(m_metadata_lock); - - auto make_nullable_string_property = [](const char *name) -> Property { - Property p = {name, PropertyType::String}; - p.is_nullable = true; - return p; - }; - - auto make_primary_key_property = [](const char *name) -> Property { - Property p = {name, PropertyType::String}; - p.is_indexed = true; - p.is_primary = true; - return p; - }; - - Realm::Config config; - config.path = std::move(path); - config.schema = Schema{ + using namespace realm; + return Schema{ {c_sync_userMetadata, { - make_primary_key_property(c_sync_identity), + {c_sync_identity, PropertyType::String}, + {c_sync_local_uuid, PropertyType::String}, {c_sync_marked_for_removal, PropertyType::Bool}, - make_nullable_string_property(c_sync_auth_server_url), - make_nullable_string_property(c_sync_user_token), + {c_sync_user_token, PropertyType::String|PropertyType::Nullable}, + {c_sync_auth_server_url, PropertyType::String}, + {c_sync_user_is_admin, PropertyType::Bool}, }}, {c_sync_fileActionMetadata, { - make_primary_key_property(c_sync_original_name), + {c_sync_original_name, PropertyType::String, Property::IsPrimary{true}}, + {c_sync_new_name, PropertyType::String|PropertyType::Nullable}, {c_sync_action, PropertyType::Int}, - make_nullable_string_property(c_sync_new_name), {c_sync_url, PropertyType::String}, {c_sync_identity, PropertyType::String}, }}, + {c_sync_clientMetadata, { + {c_sync_uuid, PropertyType::String}, + }} }; - config.schema_mode = SchemaMode::Additive; - config.schema_version = 0; +} + +} // anonymous namespace + +namespace realm { + +// MARK: - Sync metadata manager + +SyncMetadataManager::SyncMetadataManager(std::string path, + bool should_encrypt, + util::Optional> encryption_key) +{ + constexpr uint64_t SCHEMA_VERSION = 2; + + Realm::Config config; + config.path = path; + config.schema = make_schema(); + config.schema_version = SCHEMA_VERSION; + config.schema_mode = SchemaMode::Automatic; #if REALM_PLATFORM_APPLE if (should_encrypt && !encryption_key) { - encryption_key = keychain::metadata_realm_encryption_key(); + encryption_key = keychain::metadata_realm_encryption_key(File::exists(path)); } #endif if (should_encrypt) { @@ -95,35 +103,77 @@ SyncMetadataManager::SyncMetadataManager(std::string path, config.encryption_key = std::move(*encryption_key); } - // Open the Realm. + config.migration_function = [](SharedRealm old_realm, SharedRealm realm, Schema&) { + if (old_realm->schema_version() < 2) { + TableRef old_table = ObjectStore::table_for_object_type(old_realm->read_group(), c_sync_userMetadata); + TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_userMetadata); + + // Get all the SyncUserMetadata objects. + Results results(old_realm, *old_table); + + // Column indices. + size_t old_idx_identity = old_table->get_column_index(c_sync_identity); + size_t old_idx_url = old_table->get_column_index(c_sync_auth_server_url); + size_t idx_local_uuid = table->get_column_index(c_sync_local_uuid); + size_t idx_url = table->get_column_index(c_sync_auth_server_url); + + for (size_t i = 0; i < results.size(); i++) { + RowExpr entry = results.get(i); + // Set the UUID equal to the user identity for existing users. + auto identity = entry.get_string(old_idx_identity); + table->set_string(idx_local_uuid, entry.get_index(), identity); + // Migrate the auth server URLs to a non-nullable property. + auto url = entry.get_string(old_idx_url); + table->set_string(idx_url, entry.get_index(), url.is_null() ? "" : url); + } + } + }; + SharedRealm realm = Realm::get_shared_realm(config); - // Get data about the (hardcoded) schemas. - DescriptorRef descriptor = ObjectStore::table_for_object_type(realm->read_group(), - c_sync_userMetadata)->get_descriptor(); + // Get data about the (hardcoded) schemas + auto object_schema = realm->schema().find(c_sync_userMetadata); m_user_schema = { - descriptor->get_column_index(c_sync_identity), - descriptor->get_column_index(c_sync_marked_for_removal), - descriptor->get_column_index(c_sync_user_token), - descriptor->get_column_index(c_sync_auth_server_url) + object_schema->persisted_properties[0].table_column, + object_schema->persisted_properties[1].table_column, + object_schema->persisted_properties[2].table_column, + object_schema->persisted_properties[3].table_column, + object_schema->persisted_properties[4].table_column, + object_schema->persisted_properties[5].table_column, }; - descriptor = ObjectStore::table_for_object_type(realm->read_group(), c_sync_fileActionMetadata)->get_descriptor(); + object_schema = realm->schema().find(c_sync_fileActionMetadata); m_file_action_schema = { - descriptor->get_column_index(c_sync_original_name), - descriptor->get_column_index(c_sync_new_name), - descriptor->get_column_index(c_sync_action), - descriptor->get_column_index(c_sync_url), - descriptor->get_column_index(c_sync_identity) + object_schema->persisted_properties[0].table_column, + object_schema->persisted_properties[1].table_column, + object_schema->persisted_properties[2].table_column, + object_schema->persisted_properties[3].table_column, + object_schema->persisted_properties[4].table_column, + }; + + object_schema = realm->schema().find(c_sync_clientMetadata); + m_client_schema = { + object_schema->persisted_properties[0].table_column, }; m_metadata_config = std::move(config); -} -Realm::Config SyncMetadataManager::get_configuration() const -{ - std::lock_guard lock(m_metadata_lock); - return m_metadata_config; + m_client_uuid = [&]() -> std::string { + TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_clientMetadata); + if (table->is_empty()) { + realm->begin_transaction(); + if (table->is_empty()) { + size_t idx = table->add_empty_row(); + REALM_ASSERT_DEBUG(idx == 0); + auto uuid = uuid_string(); + table->set_string(m_client_schema.idx_uuid, idx, uuid); + realm->commit_transaction(); + return uuid; + } + realm->cancel_transaction(); + } + return table->get_string(m_client_schema.idx_uuid, 0); + }(); } SyncUserMetadataResults SyncMetadataManager::all_unmarked_users() const @@ -138,8 +188,7 @@ SyncUserMetadataResults SyncMetadataManager::all_users_marked_for_removal() cons SyncUserMetadataResults SyncMetadataManager::get_users(bool marked) const { - // Open the Realm. - SharedRealm realm = Realm::get_shared_realm(get_configuration()); + SharedRealm realm = Realm::get_shared_realm(m_metadata_config); TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_userMetadata); Query query = table->where().equal(m_user_schema.idx_marked_for_removal, marked); @@ -150,104 +199,206 @@ SyncUserMetadataResults SyncMetadataManager::get_users(bool marked) const SyncFileActionMetadataResults SyncMetadataManager::all_pending_actions() const { - SharedRealm realm = Realm::get_shared_realm(get_configuration()); + SharedRealm realm = Realm::get_shared_realm(m_metadata_config); TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_fileActionMetadata); Results results(realm, table->where()); return SyncFileActionMetadataResults(std::move(results), std::move(realm), m_file_action_schema); } -SyncUserMetadata::SyncUserMetadata(Schema schema, SharedRealm realm, RowExpr row) -: m_invalid(row.get_bool(schema.idx_marked_for_removal)) -, m_schema(std::move(schema)) -, m_realm(std::move(realm)) -, m_row(row) -{ } +bool SyncMetadataManager::delete_metadata_action(const std::string& original_name) const +{ + auto shared_realm = Realm::get_shared_realm(m_metadata_config); + + // Retrieve the row for this object. + TableRef table = ObjectStore::table_for_object_type(shared_realm->read_group(), c_sync_fileActionMetadata); + shared_realm->begin_transaction(); + size_t row_idx = table->find_first_string(m_file_action_schema.idx_original_name, original_name); + if (row_idx == not_found) { + shared_realm->cancel_transaction(); + return false; + } + table->move_last_over(row_idx); + shared_realm->commit_transaction(); + return true; +} -SyncUserMetadata::SyncUserMetadata(const SyncMetadataManager& manager, std::string identity, bool make_if_absent) -: m_schema(manager.m_user_schema) +util::Optional SyncMetadataManager::get_or_make_user_metadata(const std::string& identity, + const std::string& url, + bool make_if_absent) const { - // Open the Realm. - m_realm = Realm::get_shared_realm(manager.get_configuration()); + auto realm = Realm::get_shared_realm(m_metadata_config); + auto& schema = m_user_schema; // Retrieve or create the row for this object. - TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), c_sync_userMetadata); - size_t row_idx = table->find_first_string(m_schema.idx_identity, identity); - if (row_idx == not_found) { - if (!make_if_absent) { - m_invalid = true; - m_realm = nullptr; - return; - } - m_realm->begin_transaction(); - row_idx = table->find_first_string(m_schema.idx_identity, identity); - if (row_idx == not_found) { - row_idx = table->add_empty_row(); - table->set_string(m_schema.idx_identity, row_idx, identity); - m_realm->commit_transaction(); + TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_userMetadata); + Query query = table->where().equal(schema.idx_identity, identity).equal(schema.idx_auth_server_url, url); + Results results(realm, std::move(query)); + REALM_ASSERT_DEBUG(results.size() < 2); + auto row = results.first(); + + if (!row) { + if (!make_if_absent) + return none; + + realm->begin_transaction(); + // Check the results again. + row = results.first(); + if (!row) { + auto row = table->get(table->add_empty_row()); + std::string uuid = util::uuid_string(); + row.set_string(schema.idx_identity, identity); + row.set_string(schema.idx_auth_server_url, url); + row.set_string(schema.idx_local_uuid, uuid); + row.set_bool(schema.idx_user_is_admin, false); + row.set_bool(schema.idx_marked_for_removal, false); + realm->commit_transaction(); + return SyncUserMetadata(schema, std::move(realm), std::move(row)); } else { // Someone beat us to adding this user. - m_realm->cancel_transaction(); + if (row->get_bool(schema.idx_marked_for_removal)) { + // User is dead. Revive or return none. + if (make_if_absent) { + row->set_bool(schema.idx_marked_for_removal, false); + realm->commit_transaction(); + } else { + realm->cancel_transaction(); + return none; + } + } else { + // User is alive, nothing else to do. + realm->cancel_transaction(); + } + return SyncUserMetadata(schema, std::move(realm), std::move(*row)); + } + } + + // Got an existing user. + if (row->get_bool(schema.idx_marked_for_removal)) { + // User is dead. Revive or return none. + if (make_if_absent) { + realm->begin_transaction(); + row->set_bool(schema.idx_marked_for_removal, false); + realm->commit_transaction(); + } else { + return none; } } - m_row = table->get(row_idx); - if (make_if_absent) { - // User existed in the table, but had been marked for deletion. Unmark it. - m_realm->begin_transaction(); - table->set_bool(m_schema.idx_marked_for_removal, row_idx, false); - m_realm->commit_transaction(); - m_invalid = false; - } else { - m_invalid = m_row.get_bool(m_schema.idx_marked_for_removal); + return SyncUserMetadata(schema, std::move(realm), std::move(*row)); +} + +SyncFileActionMetadata SyncMetadataManager::make_file_action_metadata(const std::string &original_name, + const std::string &url, + const std::string &local_uuid, + SyncFileActionMetadata::Action action, + util::Optional new_name) const +{ + size_t raw_action = static_cast(action); + + // Open the Realm. + auto realm = Realm::get_shared_realm(m_metadata_config); + auto& schema = m_file_action_schema; + + // Retrieve or create the row for this object. + TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_fileActionMetadata); + realm->begin_transaction(); + size_t row_idx = table->find_first_string(schema.idx_original_name, original_name); + if (row_idx == not_found) { + row_idx = table->add_empty_row(); + table->set_string(schema.idx_original_name, row_idx, original_name); } + table->set_string(schema.idx_new_name, row_idx, new_name); + table->set_int(schema.idx_action, row_idx, raw_action); + table->set_string(schema.idx_url, row_idx, url); + table->set_string(schema.idx_user_identity, row_idx, local_uuid); + realm->commit_transaction(); + return SyncFileActionMetadata(schema, std::move(realm), table->get(row_idx)); } -bool SyncUserMetadata::is_valid() const +util::Optional SyncMetadataManager::get_file_action_metadata(const std::string& original_name) const { - return !m_invalid; + auto realm = Realm::get_shared_realm(m_metadata_config); + auto schema = m_file_action_schema; + TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_fileActionMetadata); + size_t row_idx = table->find_first_string(schema.idx_original_name, original_name); + if (row_idx == not_found) + return none; + + return SyncFileActionMetadata(std::move(schema), std::move(realm), table->get(row_idx)); } +// MARK: - Sync user metadata + +SyncUserMetadata::SyncUserMetadata(Schema schema, SharedRealm realm, RowExpr row) +: m_realm(std::move(realm)) +, m_schema(std::move(schema)) +, m_row(row) +{ } + std::string SyncUserMetadata::identity() const { + REALM_ASSERT(m_realm); + m_realm->verify_thread(); + return m_row.get_string(m_schema.idx_identity); +} + +std::string SyncUserMetadata::local_uuid() const +{ + REALM_ASSERT(m_realm); m_realm->verify_thread(); - StringData result = m_row.get_string(m_schema.idx_identity); - return result; + return m_row.get_string(m_schema.idx_local_uuid); } -util::Optional SyncUserMetadata::get_optional_string_field(size_t col_idx) const +util::Optional SyncUserMetadata::user_token() const { - REALM_ASSERT(!m_invalid); + REALM_ASSERT(m_realm); m_realm->verify_thread(); - StringData result = m_row.get_string(col_idx); + StringData result = m_row.get_string(m_schema.idx_user_token); return result.is_null() ? util::none : util::make_optional(std::string(result)); } -util::Optional SyncUserMetadata::server_url() const +std::string SyncUserMetadata::auth_server_url() const { - return get_optional_string_field(m_schema.idx_auth_server_url); + REALM_ASSERT(m_realm); + m_realm->verify_thread(); + return m_row.get_string(m_schema.idx_auth_server_url); } -util::Optional SyncUserMetadata::user_token() const +bool SyncUserMetadata::is_admin() const { - return get_optional_string_field(m_schema.idx_user_token); + REALM_ASSERT(m_realm); + m_realm->verify_thread(); + return m_row.get_bool(m_schema.idx_user_is_admin); } -void SyncUserMetadata::set_state(util::Optional server_url, util::Optional user_token) +void SyncUserMetadata::set_user_token(util::Optional user_token) { - if (m_invalid) { + if (m_invalid) return; - } + + REALM_ASSERT_DEBUG(m_realm); m_realm->verify_thread(); m_realm->begin_transaction(); m_row.set_string(m_schema.idx_user_token, *user_token); - m_row.set_string(m_schema.idx_auth_server_url, *server_url); + m_realm->commit_transaction(); +} + +void SyncUserMetadata::set_is_admin(bool is_admin) +{ + if (m_invalid) + return; + + REALM_ASSERT_DEBUG(m_realm); + m_realm->verify_thread(); + m_realm->begin_transaction(); + m_row.set_bool(m_schema.idx_user_is_admin, is_admin); m_realm->commit_transaction(); } void SyncUserMetadata::mark_for_removal() { - if (m_invalid) { + if (m_invalid) return; - } + m_realm->verify_thread(); m_realm->begin_transaction(); m_row.set_bool(m_schema.idx_marked_for_removal, true); @@ -264,86 +415,53 @@ void SyncUserMetadata::remove() m_realm = nullptr; } -util::Optional SyncFileActionMetadata::metadata_for_path(const std::string& original_name, const SyncMetadataManager& manager) -{ - auto realm = Realm::get_shared_realm(manager.get_configuration()); - auto schema = manager.m_file_action_schema; - TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_fileActionMetadata); - size_t row_idx = table->find_first_string(schema.idx_original_name, original_name); - if (row_idx == not_found) { - return none; - } - return SyncFileActionMetadata(std::move(schema), std::move(realm), table->get(row_idx)); -} - -SyncFileActionMetadata::SyncFileActionMetadata(const SyncMetadataManager& manager, - Action action, - const std::string& original_name, - const std::string& url, - const std::string& user_identity, - util::Optional new_name) -: m_schema(manager.m_file_action_schema) -{ - size_t raw_action = static_cast(action); - - // Open the Realm. - m_realm = Realm::get_shared_realm(manager.get_configuration()); - - // Retrieve or create the row for this object. - TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), c_sync_fileActionMetadata); - m_realm->begin_transaction(); - size_t row_idx = table->find_first_string(m_schema.idx_original_name, original_name); - if (row_idx == not_found) { - row_idx = table->add_empty_row(); - table->set_string(m_schema.idx_original_name, row_idx, original_name); - } - table->set_string(m_schema.idx_new_name, row_idx, new_name); - table->set_int(m_schema.idx_action, row_idx, raw_action); - table->set_string(m_schema.idx_url, row_idx, url); - table->set_string(m_schema.idx_user_identity, row_idx, user_identity); - m_realm->commit_transaction(); - m_row = table->get(row_idx); -} +// MARK: - File action metadata SyncFileActionMetadata::SyncFileActionMetadata(Schema schema, SharedRealm realm, RowExpr row) -: m_schema(std::move(schema)) -, m_realm(std::move(realm)) +: m_realm(std::move(realm)) +, m_schema(std::move(schema)) , m_row(row) { } std::string SyncFileActionMetadata::original_name() const { + REALM_ASSERT(m_realm); m_realm->verify_thread(); return m_row.get_string(m_schema.idx_original_name); } util::Optional SyncFileActionMetadata::new_name() const { + REALM_ASSERT(m_realm); m_realm->verify_thread(); StringData result = m_row.get_string(m_schema.idx_new_name); return result.is_null() ? util::none : util::make_optional(std::string(result)); } -SyncFileActionMetadata::Action SyncFileActionMetadata::action() const +std::string SyncFileActionMetadata::user_local_uuid() const { + REALM_ASSERT(m_realm); m_realm->verify_thread(); - return static_cast(m_row.get_int(m_schema.idx_action)); + return m_row.get_string(m_schema.idx_user_identity); } -std::string SyncFileActionMetadata::url() const +SyncFileActionMetadata::Action SyncFileActionMetadata::action() const { + REALM_ASSERT(m_realm); m_realm->verify_thread(); - return m_row.get_string(m_schema.idx_url); + return static_cast(m_row.get_int(m_schema.idx_action)); } -std::string SyncFileActionMetadata::user_identity() const +std::string SyncFileActionMetadata::url() const { + REALM_ASSERT(m_realm); m_realm->verify_thread(); - return m_row.get_string(m_schema.idx_user_identity); + return m_row.get_string(m_schema.idx_url); } void SyncFileActionMetadata::remove() { + REALM_ASSERT(m_realm); m_realm->verify_thread(); m_realm->begin_transaction(); TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), c_sync_fileActionMetadata); @@ -352,4 +470,4 @@ void SyncFileActionMetadata::remove() m_realm = nullptr; } -} +} // namespace realm diff --git a/Pods/Realm/Realm/ObjectStore/src/sync/impl/work_queue.cpp b/Pods/Realm/Realm/ObjectStore/src/sync/impl/work_queue.cpp new file mode 100644 index 0000000..b11b397 --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/sync/impl/work_queue.cpp @@ -0,0 +1,80 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2018 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "sync/impl/work_queue.hpp" + +#include + +namespace realm { +namespace _impl { +namespace partial_sync { + +WorkQueue::~WorkQueue() +{ + { + std::unique_lock lock(m_mutex); + m_stopping = true; + } + m_cv.notify_one(); + + if (m_thread.joinable()) + m_thread.join(); +} + +void WorkQueue::enqueue(std::function function) +{ + { + std::unique_lock lock(m_mutex); + m_queue.push_back(std::move(function)); + + if (m_stopped) + create_thread(); + } + m_cv.notify_one(); +} + +void WorkQueue::create_thread() +{ + if (m_thread.joinable()) + m_thread.join(); + + m_thread = std::thread([this] { + std::vector> queue; + + std::unique_lock lock(m_mutex); + while (!m_stopping && + m_cv.wait_for(lock, std::chrono::milliseconds(500), + [&] { return !m_queue.empty() || m_stopping; })) { + + swap(queue, m_queue); + + lock.unlock(); + for (auto& f : queue) + f(); + queue.clear(); + lock.lock(); + } + + m_stopped = true; + }); + m_stopped = false; +} + +} // namespace partial_sync +} // namespace _impl +} // namespace realm diff --git a/Pods/Realm/Realm/ObjectStore/src/sync/partial_sync.cpp b/Pods/Realm/Realm/ObjectStore/src/sync/partial_sync.cpp new file mode 100644 index 0000000..b5f0525 --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/sync/partial_sync.cpp @@ -0,0 +1,652 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2017 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "sync/partial_sync.hpp" + +#include "impl/collection_notifier.hpp" +#include "impl/notification_wrapper.hpp" +#include "impl/object_accessor_impl.hpp" +#include "impl/realm_coordinator.hpp" +#include "object_schema.hpp" +#include "results.hpp" +#include "shared_realm.hpp" +#include "sync/impl/work_queue.hpp" +#include "sync/subscription_state.hpp" +#include "sync/sync_config.hpp" +#include "sync/sync_session.hpp" + +#include +#include + +namespace { +constexpr const char* result_sets_type_name = "__ResultSets"; +} + +namespace realm { + +namespace _impl { + +void initialize_schema(Group& group) +{ + std::string result_sets_table_name = ObjectStore::table_name_for_object_type(result_sets_type_name); + TableRef table = group.get_table(result_sets_table_name); + if (!table) { + table = sync::create_table(group, result_sets_table_name); + table->add_column(type_String, "query"); + table->add_column(type_String, "matches_property"); + table->add_column(type_Int, "status"); + table->add_column(type_String, "error_message"); + table->add_column(type_Int, "query_parse_counter"); + } + else { + // The table already existed, so it should have all of the columns that are in the shared schema. + REALM_ASSERT(table->get_column_index("query") != npos); + REALM_ASSERT(table->get_column_index("matches_property") != npos); + REALM_ASSERT(table->get_column_index("status") != npos); + REALM_ASSERT(table->get_column_index("error_message") != npos); + REALM_ASSERT(table->get_column_index("query_parse_counter") != npos); + } + + // We may need to add the "name" column even if the __ResultSets table already existed + // as it's not added by the server when it creates the table. + if (table->get_column_index("name") == npos) { + size_t idx = table->add_column(type_String, "name"); + table->add_search_index(idx); + } +} + +// A stripped-down version of WriteTransaction that can promote an existing read transaction +// and that notifies the sync session after committing a change. +class WriteTransactionNotifyingSync { +public: + WriteTransactionNotifyingSync(Realm::Config const& config, SharedGroup& sg) + : m_config(config) + , m_shared_group(&sg) + { + if (m_shared_group->get_transact_stage() == SharedGroup::transact_Reading) + LangBindHelper::promote_to_write(*m_shared_group); + else + m_shared_group->begin_write(); + } + + ~WriteTransactionNotifyingSync() + { + if (m_shared_group) + m_shared_group->rollback(); + } + + SharedGroup::version_type commit() + { + REALM_ASSERT(m_shared_group); + auto version = m_shared_group->commit(); + m_shared_group = nullptr; + + auto session = SyncManager::shared().get_session(m_config.path, *m_config.sync_config); + SyncSession::Internal::nonsync_transact_notify(*session, version); + return version; + } + + void rollback() + { + REALM_ASSERT(m_shared_group); + m_shared_group->rollback(); + m_shared_group = nullptr; + } + + Group& get_group() const noexcept + { + REALM_ASSERT(m_shared_group); + return _impl::SharedGroupFriend::get_group(*m_shared_group); + } + +private: + Realm::Config const& m_config; + SharedGroup* m_shared_group; +}; + +// Provides a convenient way for code in this file to access private details of `Realm` +// without having to add friend declarations for each individual use. +class PartialSyncHelper { +public: + static decltype(auto) get_shared_group(Realm& realm) + { + return Realm::Internal::get_shared_group(realm); + } + + static decltype(auto) get_coordinator(Realm& realm) + { + return Realm::Internal::get_coordinator(realm); + } +}; + +struct RowHandover { + RowHandover(Realm& realm, Row row) + : source_shared_group(*PartialSyncHelper::get_shared_group(realm)) + , row(source_shared_group.export_for_handover(std::move(row))) + , version(source_shared_group.pin_version()) + { + } + + ~RowHandover() + { + // If the row isn't already null we've not been imported and the version pin will leak. + REALM_ASSERT(!row); + } + + SharedGroup& source_shared_group; + std::unique_ptr> row; + VersionID version; +}; + +} // namespace _impl + +namespace partial_sync { + +namespace { + +template +void with_open_shared_group(Realm::Config const& config, F&& function) +{ + std::unique_ptr history; + std::unique_ptr sg; + std::unique_ptr read_only_group; + Realm::open_with_config(config, history, sg, read_only_group, nullptr); + + function(*sg); +} +void update_schema(Group& group, Property matches_property) +{ + Schema current_schema; + std::string table_name = ObjectStore::table_name_for_object_type(result_sets_type_name); + if (group.has_table(table_name)) + current_schema = {ObjectSchema{group, result_sets_type_name}}; + + Schema desired_schema({ + ObjectSchema(result_sets_type_name, { + {"name", PropertyType::String, Property::IsPrimary{false}, Property::IsIndexed{true}}, + {"matches_property", PropertyType::String}, + {"query", PropertyType::String}, + {"status", PropertyType::Int}, + {"error_message", PropertyType::String}, + {"query_parse_counter", PropertyType::Int}, + std::move(matches_property) + }) + }); + auto required_changes = current_schema.compare(desired_schema); + if (!required_changes.empty()) + ObjectStore::apply_additive_changes(group, required_changes, true); +} + +struct ResultSetsColumns { + ResultSetsColumns(Table& table, std::string const& matches_property_name) + { + name = table.get_column_index("name"); + REALM_ASSERT(name != npos); + + query = table.get_column_index("query"); + REALM_ASSERT(query != npos); + + this->matches_property_name = table.get_column_index("matches_property"); + REALM_ASSERT(this->matches_property_name != npos); + + // This may be `npos` if the column does not yet exist. + matches_property = table.get_column_index(matches_property_name); + } + + size_t name; + size_t query; + size_t matches_property_name; + size_t matches_property; +}; + +bool validate_existing_subscription(Table& table, ResultSetsColumns const& columns, std::string const& name, + std::string const& query, std::string const& matches_property) +{ + auto existing_row_ndx = table.find_first_string(columns.name, name); + if (existing_row_ndx == npos) + return false; + + StringData existing_query = table.get_string(columns.query, existing_row_ndx); + if (existing_query != query) + throw std::runtime_error(util::format("An existing subscription exists with the same name, " + "but a different query ('%1' vs '%2').", + existing_query, query)); + + StringData existing_matches_property = table.get_string(columns.matches_property_name, existing_row_ndx); + if (existing_matches_property != matches_property) + throw std::runtime_error(util::format("An existing subscription exists with the same name, " + "but a different result type ('%1' vs '%2').", + existing_matches_property, matches_property)); + + return true; +} + +void enqueue_registration(Realm& realm, std::string object_type, std::string query, std::string name, + std::function callback) +{ + auto config = realm.config(); + + auto& work_queue = _impl::PartialSyncHelper::get_coordinator(realm).partial_sync_work_queue(); + work_queue.enqueue([object_type=std::move(object_type), query=std::move(query), name=std::move(name), + callback=std::move(callback), config=std::move(config)] { + try { + with_open_shared_group(config, [&](SharedGroup& sg) { + _impl::WriteTransactionNotifyingSync write(config, sg); + + auto matches_property = std::string(object_type) + "_matches"; + + auto table = ObjectStore::table_for_object_type(write.get_group(), result_sets_type_name); + ResultSetsColumns columns(*table, matches_property); + + // Update schema if needed. + if (columns.matches_property == npos) { + auto target_table = ObjectStore::table_for_object_type(write.get_group(), object_type); + columns.matches_property = table->add_column_link(type_LinkList, matches_property, *target_table); + } else { + // FIXME: Validate that the column type and link target are correct. + } + + if (!validate_existing_subscription(*table, columns, name, query, matches_property)) { + auto row_ndx = sync::create_object(write.get_group(), *table); + table->set_string(columns.name, row_ndx, name); + table->set_string(columns.query, row_ndx, query); + table->set_string(columns.matches_property_name, row_ndx, matches_property); + } + + write.commit(); + }); + } catch (...) { + callback(std::current_exception()); + return; + } + + callback(nullptr); + }); +} + +void enqueue_unregistration(Object result_set, std::function callback) +{ + auto realm = result_set.realm(); + auto config = realm->config(); + auto& work_queue = _impl::PartialSyncHelper::get_coordinator(*realm).partial_sync_work_queue(); + + // Export a reference to the __ResultSets row so we can hand it to the worker thread. + // We store it in a shared_ptr as it would otherwise prevent the lambda from being copyable, + // which `std::function` requires. + auto handover = std::make_shared<_impl::RowHandover>(*realm, result_set.row()); + + work_queue.enqueue([handover=std::move(handover), callback=std::move(callback), + config=std::move(config)] () { + with_open_shared_group(config, [&](SharedGroup& sg) { + // Import handed-over object. + sg.begin_read(handover->version); + Row row = *sg.import_from_handover(std::move(handover->row)); + sg.unpin_version(handover->version); + + _impl::WriteTransactionNotifyingSync write(config, sg); + if (row.is_attached()) { + row.move_last_over(); + write.commit(); + } + else { + write.rollback(); + } + }); + callback(); + }); +} + +std::string default_name_for_query(const std::string& query, const std::string& object_type) +{ + return util::format("[%1] %2", object_type, query); +} + +} // unnamed namespace + +void register_query(std::shared_ptr realm, const std::string &object_class, const std::string &query, + std::function callback) +{ + auto sync_config = realm->config().sync_config; + if (!sync_config || !sync_config->is_partial) + throw std::logic_error("A partial sync query can only be registered in a partially synced Realm"); + + if (realm->schema().find(object_class) == realm->schema().end()) + throw std::logic_error("A partial sync query can only be registered for a type that exists in the Realm's schema"); + + auto matches_property = object_class + "_matches"; + + // The object schema must outlive `object` below. + std::unique_ptr result_sets_schema; + Object raw_object; + { + realm->begin_transaction(); + auto cleanup = util::make_scope_exit([&]() noexcept { + if (realm->is_in_transaction()) + realm->cancel_transaction(); + }); + + update_schema(realm->read_group(), + Property(matches_property, PropertyType::Object|PropertyType::Array, object_class)); + + result_sets_schema = std::make_unique(realm->read_group(), result_sets_type_name); + + CppContext context; + raw_object = Object::create(context, realm, *result_sets_schema, + AnyDict{ + {"name", query}, + {"matches_property", matches_property}, + {"query", query}, + {"status", int64_t(0)}, + {"error_message", std::string()}, + {"query_parse_counter", int64_t(0)}, + }, false); + + realm->commit_transaction(); + } + + auto object = std::make_shared<_impl::NotificationWrapper>(std::move(raw_object)); + + // Observe the new object and notify listener when the results are complete (status != 0). + auto notification_callback = [object, matches_property, + result_sets_schema=std::move(result_sets_schema), + callback=std::move(callback)](CollectionChangeSet, std::exception_ptr error) mutable { + if (error) { + callback(Results(), error); + object.reset(); + return; + } + + CppContext context; + auto status = any_cast(object->get_property_value(context, "status")); + if (status == 0) { + // Still computing... + return; + } else if (status == 1) { + // Finished successfully. + auto list = any_cast(object->get_property_value(context, matches_property)); + callback(list.as_results(), nullptr); + } else { + // Finished with error. + auto message = any_cast(object->get_property_value(context, "error_message")); + callback(Results(), std::make_exception_ptr(std::runtime_error(std::move(message)))); + } + object.reset(); + }; + object->add_notification_callback(std::move(notification_callback)); +} + +struct Subscription::Notifier : public _impl::CollectionNotifier { + enum State { + Creating, + Complete, + Removed, + }; + + Notifier(std::shared_ptr realm) + : _impl::CollectionNotifier(std::move(realm)) + , m_coordinator(&_impl::PartialSyncHelper::get_coordinator(*get_realm())) + { + } + + void release_data() noexcept override { } + void run() override + { + std::unique_lock lock(m_mutex); + if (m_has_results_to_deliver) { + // Mark the object as being modified so that CollectionNotifier is aware + // that there are changes to deliver. + m_changes.modify(0); + } + } + + void deliver(SharedGroup&) override + { + std::unique_lock lock(m_mutex); + m_error = m_pending_error; + m_pending_error = nullptr; + + m_state = m_pending_state; + m_has_results_to_deliver = false; + } + + void finished_subscribing(std::exception_ptr error) + { + { + std::unique_lock lock(m_mutex); + m_pending_error = error; + m_pending_state = Complete; + m_has_results_to_deliver = true; + } + + // Trigger processing of change notifications. + m_coordinator->wake_up_notifier_worker(); + } + + void finished_unsubscribing() + { + { + std::unique_lock lock(m_mutex); + + m_pending_state = Removed; + m_has_results_to_deliver = true; + } + + // Trigger processing of change notifications. + m_coordinator->wake_up_notifier_worker(); + } + + std::exception_ptr error() const + { + std::unique_lock lock(m_mutex); + return m_error; + } + + State state() const + { + std::unique_lock lock(m_mutex); + return m_state; + } + +private: + void do_attach_to(SharedGroup&) override { } + void do_detach_from(SharedGroup&) override { } + + void do_prepare_handover(SharedGroup&) override + { + add_changes(std::move(m_changes)); + } + + bool do_add_required_change_info(_impl::TransactionChangeInfo&) override { return false; } + bool prepare_to_deliver() override { return m_has_results_to_deliver; } + + _impl::RealmCoordinator *m_coordinator; + + mutable std::mutex m_mutex; + _impl::CollectionChangeBuilder m_changes; + std::exception_ptr m_pending_error = nullptr; + std::exception_ptr m_error = nullptr; + bool m_has_results_to_deliver = false; + + State m_state = Creating; + State m_pending_state = Creating; +}; + +Subscription subscribe(Results const& results, util::Optional user_provided_name) +{ + auto realm = results.get_realm(); + + auto sync_config = realm->config().sync_config; + if (!sync_config || !sync_config->is_partial) + throw std::logic_error("A partial sync query can only be registered in a partially synced Realm"); + + auto query = results.get_query().get_description(); // Throws if the query cannot be serialized. + query += " " + results.get_descriptor_ordering().get_description(results.get_query().get_table()); + + std::string name = user_provided_name ? std::move(*user_provided_name) + : default_name_for_query(query, results.get_object_type()); + + Subscription subscription(name, results.get_object_type(), realm); + std::weak_ptr weak_notifier = subscription.m_notifier; + enqueue_registration(*realm, results.get_object_type(), std::move(query), std::move(name), + [weak_notifier=std::move(weak_notifier)](std::exception_ptr error) { + if (auto notifier = weak_notifier.lock()) + notifier->finished_subscribing(error); + }); + return subscription; +} + +void unsubscribe(Subscription& subscription) +{ + if (auto result_set_object = subscription.result_set_object()) { + // The subscription has its result set object, so we can queue up the unsubscription immediately. + std::weak_ptr weak_notifier = subscription.m_notifier; + enqueue_unregistration(*result_set_object, [weak_notifier=std::move(weak_notifier)]() { + if (auto notifier = weak_notifier.lock()) + notifier->finished_unsubscribing(); + }); + return; + } + + switch (subscription.state()) { + case SubscriptionState::Creating: { + // The result set object is in the process of being created. Try unsubscribing again once it exists. + auto token = std::make_shared(); + *token = subscription.add_notification_callback([token, &subscription] () { + if (subscription.state() == SubscriptionState::Creating) + return; + + unsubscribe(subscription); + + // Invalidate the notification token so we do not receive further callbacks. + *token = SubscriptionNotificationToken(); + }); + return; + } + + case SubscriptionState::Error: + // We encountered an error when creating the subscription. There's nothing to remove, so just + // mark the subscription as removed. + subscription.m_notifier->finished_unsubscribing(); + break; + + case SubscriptionState::Invalidated: + // Nothing to do. We have already removed the subscription. + break; + + case SubscriptionState::Pending: + case SubscriptionState::Complete: + // This should not be reachable as these states require the result set object to exist. + REALM_ASSERT(false); + break; + } +} + +Subscription::Subscription(std::string name, std::string object_type, std::shared_ptr realm) +: m_object_schema(realm->read_group(), result_sets_type_name) +{ + // FIXME: Why can't I do this in the initializer list? + m_notifier = std::make_shared(realm); + _impl::RealmCoordinator::register_notifier(m_notifier); + + auto matches_property = std::string(object_type) + "_matches"; + + TableRef table = ObjectStore::table_for_object_type(realm->read_group(), result_sets_type_name); + Query query = table->where(); + query.equal(m_object_schema.property_for_name("name")->table_column, name); + query.equal(m_object_schema.property_for_name("matches_property")->table_column, matches_property); + m_result_sets = Results(std::move(realm), std::move(query)); +} + +Subscription::~Subscription() = default; +Subscription::Subscription(Subscription&&) = default; +Subscription& Subscription::operator=(Subscription&&) = default; + +SubscriptionNotificationToken Subscription::add_notification_callback(std::function callback) +{ + auto result_sets_token = m_result_sets.add_notification_callback([callback] (CollectionChangeSet, std::exception_ptr) { + callback(); + }); + NotificationToken registration_token(m_notifier, m_notifier->add_callback([callback] (CollectionChangeSet, std::exception_ptr) { + callback(); + })); + return SubscriptionNotificationToken{std::move(registration_token), std::move(result_sets_token)}; +} + +util::Optional Subscription::result_set_object() const +{ + if (m_notifier->state() == Notifier::Complete) { + if (auto row = m_result_sets.first()) + return Object(m_result_sets.get_realm(), m_object_schema, *row); + } + + return util::none; +} + +SubscriptionState Subscription::state() const +{ + switch (m_notifier->state()) { + case Notifier::Creating: + return SubscriptionState::Creating; + case Notifier::Removed: + return SubscriptionState::Invalidated; + case Notifier::Complete: + break; + } + + if (m_notifier->error()) + return SubscriptionState::Error; + + if (auto object = result_set_object()) { + CppContext context; + auto value = any_cast(object->get_property_value(context, "status")); + return (SubscriptionState)value; + } + + // We may not have an object even if the subscription has completed if the completion callback fired + // but the result sets callback is yet to fire. + return SubscriptionState::Creating; +} + +std::exception_ptr Subscription::error() const +{ + if (auto error = m_notifier->error()) + return error; + + if (auto object = result_set_object()) { + CppContext context; + auto message = any_cast(object->get_property_value(context, "error_message")); + if (message.size()) + return make_exception_ptr(std::runtime_error(message)); + } + + return nullptr; +} + +Results Subscription::results() const +{ + auto object = result_set_object(); + REALM_ASSERT_RELEASE(object); + + CppContext context; + auto matches_property = any_cast(object->get_property_value(context, "matches_property")); + auto list = any_cast(object->get_property_value(context, matches_property)); + return list.as_results(); +} + +} // namespace partial_sync +} // namespace realm diff --git a/Pods/Realm/Realm/ObjectStore/src/sync/sync_config.cpp b/Pods/Realm/Realm/ObjectStore/src/sync/sync_config.cpp new file mode 100644 index 0000000..b45bd3c --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/sync/sync_config.cpp @@ -0,0 +1,57 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2017 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "sync/sync_config.hpp" + +#include "sync/sync_manager.hpp" + +#include + +namespace realm { + +std::string SyncConfig::partial_sync_identifier(const SyncUser& user) +{ + std::string raw_identifier = SyncManager::shared().client_uuid() + "/" + user.local_identity(); + uint8_t identifier[20]; + sync::crypto::sha1(raw_identifier.data(), raw_identifier.size(), (char *)&identifier[0]); + + std::stringstream ss; + ss << std::hex << std::setfill('0'); + for (uint8_t c : identifier) + ss << std::setw(2) << (unsigned)c; + return ss.str(); +} + +std::string SyncConfig::realm_url() const +{ + REALM_ASSERT(reference_realm_url.length() > 0); + REALM_ASSERT(user); + + if (!is_partial) + return reference_realm_url; + + std::string base_url = reference_realm_url; + if (base_url.back() == '/') + base_url.pop_back(); + + if (custom_partial_sync_identifier) + return util::format("%1/__partial/%2", base_url, *custom_partial_sync_identifier); + return util::format("%1/__partial/%2/%3", base_url, user->identity(), partial_sync_identifier(*user)); +} + +} // namespace realm diff --git a/Pods/Realm/Realm/ObjectStore/src/sync/sync_manager.cpp b/Pods/Realm/Realm/ObjectStore/src/sync/sync_manager.cpp index 75bc25c..7fe4343 100644 --- a/Pods/Realm/Realm/ObjectStore/src/sync/sync_manager.cpp +++ b/Pods/Realm/Realm/ObjectStore/src/sync/sync_manager.cpp @@ -24,11 +24,11 @@ #include "sync/sync_session.hpp" #include "sync/sync_user.hpp" -#include - using namespace realm; using namespace realm::_impl; +constexpr const char SyncManager::c_admin_identity[]; + SyncManager& SyncManager::shared() { // The singleton is heap-allocated in order to fix an issue when running unit tests where tests would crash after @@ -45,7 +45,8 @@ void SyncManager::configure_file_system(const std::string& base_file_path, struct UserCreationData { std::string identity; std::string user_token; - util::Optional server_url; + std::string server_url; + bool is_admin; }; std::vector users_to_add; @@ -72,7 +73,7 @@ void SyncManager::configure_file_system(const std::string& base_file_path, try { m_metadata_manager = std::make_unique(m_file_manager->metadata_path(), true, - std::move(custom_encryption_key)); + custom_encryption_key); } catch (RealmFileException const& ex) { if (reset_metadata_on_error && m_file_manager->remove_metadata_realm()) { m_metadata_manager = std::make_unique(m_file_manager->metadata_path(), @@ -88,6 +89,8 @@ void SyncManager::configure_file_system(const std::string& base_file_path, } REALM_ASSERT(m_metadata_manager); + m_client_uuid = m_metadata_manager->client_uuid(); + // Perform any necessary file actions. std::vector completed_actions; SyncFileActionMetadataResults file_actions = m_metadata_manager->all_pending_actions(); @@ -107,9 +110,15 @@ void SyncManager::configure_file_system(const std::string& base_file_path, auto user_data = users.get(i); auto user_token = user_data.user_token(); auto identity = user_data.identity(); - auto server_url = user_data.server_url(); + auto server_url = user_data.auth_server_url(); + bool is_admin = user_data.is_admin(); if (user_token) { - UserCreationData data = { std::move(identity), std::move(*user_token), std::move(server_url) }; + UserCreationData data = { + std::move(identity), + std::move(*user_token), + std::move(server_url), + is_admin, + }; users_to_add.emplace_back(std::move(data)); } } @@ -122,7 +131,7 @@ void SyncManager::configure_file_system(const std::string& base_file_path, // FIXME: delete user data in a different way? (This deletes a logged-out user's data as soon as the app // launches again, which might not be how some apps want to treat their data.) try { - m_file_manager->remove_user_directory(user.identity()); + m_file_manager->remove_user_directory(user.local_uuid()); dead_users.emplace_back(std::move(user)); } catch (util::File::AccessError const&) { continue; @@ -135,9 +144,11 @@ void SyncManager::configure_file_system(const std::string& base_file_path, { std::lock_guard lock(m_user_mutex); for (auto& user_data : users_to_add) { - m_users.insert({ user_data.identity, std::make_shared(user_data.user_token, - user_data.identity, - user_data.server_url) }); + auto& identity = user_data.identity; + auto& server_url = user_data.server_url; + auto user = std::make_shared(user_data.user_token, identity, server_url); + user->set_is_admin(user_data.is_admin); + m_users.insert({ {identity, server_url}, std::move(user) }); } } } @@ -147,13 +158,11 @@ bool SyncManager::immediately_run_file_actions(const std::string& realm_path) if (!m_metadata_manager) { return false; } - auto metadata = SyncFileActionMetadata::metadata_for_path(realm_path, *m_metadata_manager); - if (!metadata) { - return false; - } - if (run_file_action(*metadata)) { - metadata->remove(); - return true; + if (auto metadata = m_metadata_manager->get_file_action_metadata(realm_path)) { + if (run_file_action(*metadata)) { + metadata->remove(); + return true; + } } return false; } @@ -166,14 +175,14 @@ bool SyncManager::run_file_action(const SyncFileActionMetadata& md) // Delete all the files for the given Realm. m_file_manager->remove_realm(md.original_name()); return true; - case SyncFileActionMetadata::Action::HandleRealmForClientReset: + case SyncFileActionMetadata::Action::BackUpThenDeleteRealm: // Copy the primary Realm file to the recovery dir, and then delete the Realm. auto new_name = md.new_name(); auto original_name = md.original_name(); if (!util::File::exists(original_name)) { // The Realm file doesn't exist anymore. return true; - } + } if (new_name && !util::File::exists(*new_name) && m_file_manager->copy_realm_file(original_name, *new_name)) { // We successfully copied the Realm file to the recovery directory. m_file_manager->remove_realm(original_name); @@ -189,10 +198,13 @@ void SyncManager::reset_for_testing() std::lock_guard lock(m_file_system_mutex); m_file_manager = nullptr; m_metadata_manager = nullptr; + m_client_uuid = util::none; + { // Destroy all the users. std::lock_guard lock(m_user_mutex); m_users.clear(); + m_admin_token_users.clear(); } { std::lock_guard lock(m_mutex); @@ -204,21 +216,19 @@ void SyncManager::reset_for_testing() { std::lock_guard lock(m_session_mutex); -#if REALM_ASSERTIONS_ENABLED // Callers of `SyncManager::reset_for_testing` should ensure there are no active sessions // prior to calling `reset_for_testing`. - auto no_active_sessions = std::all_of(m_active_sessions.begin(), m_active_sessions.end(), [](auto& element){ - return element.second.expired(); + auto no_active_sessions = std::none_of(m_sessions.begin(), m_sessions.end(), [](auto& element){ + return element.second->existing_external_reference(); }); - REALM_ASSERT(no_active_sessions); -#endif + REALM_ASSERT_RELEASE(no_active_sessions); - // Destroy any remaining inactive sessions. + // Destroy any inactive sessions. // FIXME: We shouldn't have any inactive sessions at this point! Sessions are expected to // remain inactive until their final upload completes, at which point they are unregistered // and destroyed. Our call to `sync::Client::stop` above aborts all uploads, so all sessions // should have already been destroyed. - m_inactive_sessions.clear(); + m_sessions.clear(); } // Destroy the client now that we have no remaining sessions. @@ -229,7 +239,7 @@ void SyncManager::reset_for_testing() m_log_level = util::Logger::Level::info; m_logger_factory = nullptr; m_client_reconnect_mode = ReconnectMode::normal; - m_client_validate_ssl = true; + m_multiplex_sessions = false; } } @@ -245,28 +255,12 @@ void SyncManager::set_logger_factory(SyncLoggerFactory& factory) noexcept m_logger_factory = &factory; } -void SyncManager::set_client_should_reconnect_immediately(bool reconnect_immediately) -{ - std::lock_guard lock(m_mutex); - m_client_reconnect_mode = reconnect_immediately ? ReconnectMode::immediate : ReconnectMode::normal; -} - -bool SyncManager::client_should_reconnect_immediately() const noexcept -{ - std::lock_guard lock(m_mutex); - return m_client_reconnect_mode == ReconnectMode::immediate; -} - -void SyncManager::set_client_should_validate_ssl(bool validate_ssl) +void SyncManager::reconnect() { - std::lock_guard lock(m_mutex); - m_client_validate_ssl = validate_ssl; -} - -bool SyncManager::client_should_validate_ssl() const noexcept -{ - std::lock_guard lock(m_mutex); - return m_client_validate_ssl; + std::lock_guard lock(m_session_mutex); + for (auto& it : m_sessions) { + it.second->handle_reconnect(); + } } util::Logger::Level SyncManager::log_level() const noexcept @@ -285,26 +279,21 @@ bool SyncManager::perform_metadata_update(std::function SyncManager::get_user(const std::string& identity, - std::string refresh_token, - util::Optional auth_server_url, - bool is_admin) +std::shared_ptr SyncManager::get_user(const SyncUserIdentifier& identifier, std::string refresh_token) { std::lock_guard lock(m_user_mutex); - auto it = m_users.find(identity); + auto it = m_users.find(identifier); if (it == m_users.end()) { // No existing user. - auto new_user = std::make_shared(std::move(refresh_token), identity, auth_server_url, is_admin); - m_users.insert({ identity, new_user }); + auto new_user = std::make_shared(std::move(refresh_token), + identifier.user_id, + identifier.auth_server_url, + none, + SyncUser::TokenType::Normal); + m_users.insert({ identifier, new_user }); return new_user; } else { auto user = it->second; - if (auth_server_url && *auth_server_url != user->server_url()) { - throw std::invalid_argument("Cannot retrieve an existing user specifying a different auth server."); - } - if (is_admin != user->is_admin()) { - throw std::invalid_argument("Cannot retrieve an existing user with a different admin status."); - } if (user->state() == SyncUser::State::Error) { return nullptr; } @@ -313,143 +302,191 @@ std::shared_ptr SyncManager::get_user(const std::string& identity, } } -std::shared_ptr SyncManager::get_existing_logged_in_user(const std::string& identity) const +std::shared_ptr SyncManager::get_admin_token_user_from_identity(const std::string& identity, + util::Optional server_url, + const std::string& token) { + if (server_url) + return get_admin_token_user(*server_url, token, identity); + std::lock_guard lock(m_user_mutex); - auto it = m_users.find(identity); - if (it == m_users.end()) { - return nullptr; + // Look up the user based off the identity. + // No server URL, so no migration possible. + auto it = m_admin_token_users.find(identity); + if (it == m_admin_token_users.end()) { + // No existing user. + auto new_user = std::make_shared(token, + c_admin_identity, + std::move(server_url), + identity, + SyncUser::TokenType::Admin); + m_admin_token_users.insert({ identity, new_user }); + return new_user; + } else { + return it->second; } - auto ptr = it->second; - return (ptr->state() == SyncUser::State::Active ? ptr : nullptr); +} + +std::shared_ptr SyncManager::get_admin_token_user(const std::string& server_url, + const std::string& token, + util::Optional old_identity) +{ + std::shared_ptr user; + { + std::lock_guard lock(m_user_mutex); + // Look up the user based off the server URL. + auto it = m_admin_token_users.find(server_url); + if (it != m_admin_token_users.end()) + return it->second; + + // No existing user. + user = std::make_shared(token, + c_admin_identity, + server_url, + c_admin_identity + server_url, + SyncUser::TokenType::Admin); + m_admin_token_users.insert({ server_url, user }); + } + if (old_identity) { + // Try renaming the user's directory to use our new naming standard, if applicable. + std::lock_guard fm_lock(m_file_system_mutex); + if (m_file_manager) + m_file_manager->try_rename_user_directory(*old_identity, c_admin_identity + server_url); + } + return user; } std::vector> SyncManager::all_logged_in_users() const { std::lock_guard lock(m_user_mutex); std::vector> users; - users.reserve(m_users.size()); + users.reserve(m_users.size() + m_admin_token_users.size()); for (auto& it : m_users) { auto user = it.second; if (user->state() == SyncUser::State::Active) { users.emplace_back(std::move(user)); } } + for (auto& it : m_admin_token_users) { + users.emplace_back(std::move(it.second)); + } return users; } std::shared_ptr SyncManager::get_current_user() const { std::lock_guard lock(m_user_mutex); - + auto is_active_user = [](auto& el) { return el.second->state() == SyncUser::State::Active; }; auto it = std::find_if(m_users.begin(), m_users.end(), is_active_user); - if (it == m_users.end()) { + if (it == m_users.end()) return nullptr; - } - if (std::find_if(std::next(it), m_users.end(), is_active_user) != m_users.end()) { + + if (std::find_if(std::next(it), m_users.end(), is_active_user) != m_users.end()) throw std::logic_error("Current user is not valid if more that one valid, logged-in user exists."); - } + return it->second; } -std::string SyncManager::path_for_realm(const std::string& user_identity, const std::string& raw_realm_url) const +std::shared_ptr SyncManager::get_existing_logged_in_user(const SyncUserIdentifier& identifier) const +{ + std::lock_guard lock(m_user_mutex); + auto it = m_users.find(identifier); + if (it == m_users.end()) + return nullptr; + + auto user = it->second; + return user->state() == SyncUser::State::Active ? user : nullptr; +} + +std::string SyncManager::path_for_realm(const SyncUser& user, const std::string& raw_realm_url) const { std::lock_guard lock(m_file_system_mutex); REALM_ASSERT(m_file_manager); - return m_file_manager->path(user_identity, raw_realm_url); + const auto& user_local_identity = user.local_identity(); + util::Optional user_info; + if (user.token_type() == SyncUser::TokenType::Normal) + user_info = SyncUserIdentifier{ user.identity(), user.server_url() }; + + return m_file_manager->path(user_local_identity, raw_realm_url, std::move(user_info)); } std::string SyncManager::recovery_directory_path() const { std::lock_guard lock(m_file_system_mutex); REALM_ASSERT(m_file_manager); - return m_file_manager->recovery_directory_path(); + return m_file_manager->recovery_directory_path(); } std::shared_ptr SyncManager::get_existing_active_session(const std::string& path) const { std::lock_guard lock(m_session_mutex); - return get_existing_active_session_locked(path); + if (auto session = get_existing_session_locked(path)) { + if (auto external_reference = session->existing_external_reference()) + return external_reference; + } + return nullptr; } -std::shared_ptr SyncManager::get_existing_active_session_locked(const std::string& path) const +std::shared_ptr SyncManager::get_existing_session_locked(const std::string& path) const { REALM_ASSERT(!m_session_mutex.try_lock()); - auto it = m_active_sessions.find(path); - if (it == m_active_sessions.end()) { - return nullptr; - } - if (auto session = it->second.lock()) { - return session; - } - return nullptr; + auto it = m_sessions.find(path); + return it == m_sessions.end() ? nullptr : it->second; } -std::unique_ptr SyncManager::get_existing_inactive_session_locked(const std::string& path) +std::shared_ptr SyncManager::get_existing_session(const std::string& path) const { - REALM_ASSERT(!m_session_mutex.try_lock()); - auto it = m_inactive_sessions.find(path); - if (it == m_inactive_sessions.end()) { - return nullptr; - } - auto ret = std::move(it->second); - m_inactive_sessions.erase(it); - return ret; + std::lock_guard lock(m_session_mutex); + if (auto session = get_existing_session_locked(path)) + return session->external_reference(); + + return nullptr; } std::shared_ptr SyncManager::get_session(const std::string& path, const SyncConfig& sync_config) { auto& client = get_sync_client(); // Throws - // The session is declared outside the scope of the lock so that if an exception is thrown - // it'll be destroyed after the lock has been dropped. This avoids deadlocking when - // dropped_last_reference_to_session attempts to lock the mutex. - std::shared_ptr shared_session; - std::lock_guard lock(m_session_mutex); - if (auto session = get_existing_active_session_locked(path)) { - return session; + if (auto session = get_existing_session_locked(path)) { + sync_config.user->register_session(session); + return session->external_reference(); } - std::unique_ptr session = get_existing_inactive_session_locked(path); - bool session_is_new = false; - if (!session) { - session_is_new = true; - session.reset(new SyncSession(client, path, sync_config)); - } + auto shared_session = SyncSession::create(client, path, sync_config); + m_sessions[path] = shared_session; - auto session_deleter = [this](SyncSession *session) { dropped_last_reference_to_session(session); }; - shared_session = std::shared_ptr(session.release(), std::move(session_deleter)); - m_active_sessions[path] = shared_session; - if (session_is_new) { - sync_config.user->register_session(shared_session); - } else { - SyncSession::revive_if_needed(shared_session); - } - return shared_session; -} + // Create the external reference immediately to ensure that the session will become + // inactive if an exception is thrown in the following code. + auto external_reference = shared_session->external_reference(); -void SyncManager::dropped_last_reference_to_session(SyncSession* session) -{ - { - std::lock_guard lock(m_session_mutex); - auto path = session->path(); - REALM_ASSERT_DEBUG(m_active_sessions.count(path)); - m_active_sessions.erase(path); - m_inactive_sessions[path].reset(session); - } - session->close(); + sync_config.user->register_session(std::move(shared_session)); + + return external_reference; } void SyncManager::unregister_session(const std::string& path) { std::lock_guard lock(m_session_mutex); - if (m_active_sessions.count(path)) + auto it = m_sessions.find(path); + REALM_ASSERT(it != m_sessions.end()); + + // If the session has an active external reference, leave it be. This will happen if the session + // moves to an inactive state while still externally reference, for instance, as a result of + // the session's user being logged out. + if (it->second->existing_external_reference()) return; - auto it = m_inactive_sessions.find(path); - REALM_ASSERT(it != m_inactive_sessions.end()); - m_inactive_sessions.erase(path); + + m_sessions.erase(path); +} + +void SyncManager::enable_session_multiplexing() +{ + std::lock_guard lock(m_mutex); + if (m_sync_client) + throw std::logic_error("Cannot enable session multiplexing after creating the sync client"); + m_multiplex_sessions = true; } SyncClient& SyncManager::get_sync_client() const @@ -473,7 +510,11 @@ std::unique_ptr SyncManager::create_sync_client() const stderr_logger->set_level_threshold(m_log_level); logger = std::move(stderr_logger); } - return std::make_unique(std::move(logger), - m_client_reconnect_mode, - m_client_validate_ssl); + return std::make_unique(std::move(logger), m_client_reconnect_mode, m_multiplex_sessions); +} + +std::string SyncManager::client_uuid() const +{ + REALM_ASSERT(m_client_uuid); + return *m_client_uuid; } diff --git a/Pods/Realm/Realm/ObjectStore/src/sync/sync_permission.cpp b/Pods/Realm/Realm/ObjectStore/src/sync/sync_permission.cpp new file mode 100644 index 0000000..d53dad0 --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/sync/sync_permission.cpp @@ -0,0 +1,370 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2017 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "sync/sync_permission.hpp" + +#include "impl/notification_wrapper.hpp" +#include "impl/object_accessor_impl.hpp" +#include "object_schema.hpp" +#include "property.hpp" + +#include "sync/sync_config.hpp" +#include "sync/sync_manager.hpp" +#include "sync/sync_session.hpp" +#include "sync/sync_user.hpp" +#include "util/event_loop_signal.hpp" +#include "util/uuid.hpp" + +#include + +using namespace realm; +using namespace std::chrono; + +// MARK: - Utility + +namespace { + +// Make a handler that extracts either an exception pointer, or the string value +// of the property with the specified name. +Permissions::AsyncOperationHandler make_handler_extracting_property(std::string property, + Permissions::PermissionOfferCallback callback) +{ + return [property=std::move(property), + callback=std::move(callback)](Object* object, std::exception_ptr exception) { + if (exception) { + callback(none, exception); + } else { + CppContext context; + auto token = any_cast(object->get_property_value(context, property)); + callback(util::make_optional(std::move(token)), nullptr); + } + }; +} + +AccessLevel extract_access_level(Object& permission, CppContext& context) +{ + auto may_manage = permission.get_property_value(context, "mayManage"); + if (may_manage.has_value() && any_cast(may_manage)) + return AccessLevel::Admin; + + auto may_write = permission.get_property_value(context, "mayWrite"); + if (may_write.has_value() && any_cast(may_write)) + return AccessLevel::Write; + + auto may_read = permission.get_property_value(context, "mayRead"); + if (may_read.has_value() && any_cast(may_read)) + return AccessLevel::Read; + + return AccessLevel::None; +} + +/// Turn a system time point value into the 64-bit integer representing ns since the Unix epoch. +int64_t ns_since_unix_epoch(const system_clock::time_point& point) +{ + tm unix_epoch{}; + unix_epoch.tm_year = 70; + time_t epoch_time = mktime(&unix_epoch); + auto epoch_point = system_clock::from_time_t(epoch_time); + return duration_cast(point - epoch_point).count(); +} + +} // anonymous namespace + +// MARK: - Permission + +Permission::Permission(Object& permission) +{ + CppContext context; + path = any_cast(permission.get_property_value(context, "path")); + access = extract_access_level(permission, context); + condition = Condition(any_cast(permission.get_property_value(context, "userId"))); + updated_at = any_cast(permission.get_property_value(context, "updatedAt")); +} + +Permission::Permission(std::string path, AccessLevel access, Condition condition, Timestamp updated_at) +: path(std::move(path)) +, access(access) +, condition(std::move(condition)) +, updated_at(std::move(updated_at)) +{ } + +std::string Permission::description_for_access_level(AccessLevel level) +{ + switch (level) { + case AccessLevel::None: return "none"; + case AccessLevel::Read: return "read"; + case AccessLevel::Write: return "write"; + case AccessLevel::Admin: return "admin"; + } + REALM_UNREACHABLE(); +} + +bool Permission::paths_are_equivalent(std::string path_1, std::string path_2, + const std::string& user_id_1, const std::string& user_id_2) +{ + REALM_ASSERT_DEBUG(path_1.length() > 0); + REALM_ASSERT_DEBUG(path_2.length() > 0); + if (path_1 == path_2) { + // If both paths are identical and contain `/~/`, the user IDs must match. + return (path_1.find("/~/") == std::string::npos) || (user_id_1 == user_id_2); + } + // Make substitutions for the first `/~/` in the string. + size_t index = path_1.find("/~/"); + if (index != std::string::npos) + path_1.replace(index + 1, 1, user_id_1); + + index = path_2.find("/~/"); + if (index != std::string::npos) + path_2.replace(index + 1, 1, user_id_2); + + return path_1 == path_2; +} + +// MARK: - Permissions + +void Permissions::get_permissions(std::shared_ptr user, + PermissionResultsCallback callback, + const ConfigMaker& make_config) +{ + auto realm = Permissions::permission_realm(user, make_config); + auto table = ObjectStore::table_for_object_type(realm->read_group(), "Permission"); + auto results = std::make_shared<_impl::NotificationWrapper>(std::move(realm), *table); + + // `get_permissions` works by temporarily adding an async notifier to the permission Realm. + // This notifier will run the `async` callback until the Realm contains permissions or + // an error happens. When either of these two things happen, the notifier will be + // unregistered by nulling out the `results_wrapper` container. + auto async = [results, callback=std::move(callback)](CollectionChangeSet, std::exception_ptr ex) mutable { + if (ex) { + callback(Results(), ex); + results.reset(); + return; + } + if (results->size() > 0) { + // We monitor the raw results. The presence of a `__management` Realm indicates + // that the permissions have been downloaded (hence, we wait until size > 0). + TableRef table = ObjectStore::table_for_object_type(results->get_realm()->read_group(), "Permission"); + size_t col_idx = table->get_descriptor()->get_column_index("path"); + auto query = !(table->column(col_idx).ends_with("/__permission") + || table->column(col_idx).ends_with("/__perm") + || table->column(col_idx).ends_with("/__management")); + // Call the callback with our new permissions object. This object will exclude the + // private Realms. + callback(results->filter(std::move(query)), nullptr); + results.reset(); + } + }; + results->add_notification_callback(std::move(async)); +} + +void Permissions::set_permission(std::shared_ptr user, + Permission permission, + PermissionChangeCallback callback, + const ConfigMaker& make_config) +{ + auto props = AnyDict{ + {"userId", permission.condition.user_id}, + {"realmUrl", user->server_url() + permission.path}, + {"mayRead", permission.access != AccessLevel::None}, + {"mayWrite", permission.access == AccessLevel::Write || permission.access == AccessLevel::Admin}, + {"mayManage", permission.access == AccessLevel::Admin}, + }; + if (permission.condition.type == Permission::Condition::Type::KeyValue) { + props.insert({"metadataKey", permission.condition.key_value.first}); + props.insert({"metadataValue", permission.condition.key_value.second}); + } + auto cb = [callback=std::move(callback)](Object*, std::exception_ptr exception) { + callback(exception); + }; + perform_async_operation("PermissionChange", std::move(user), std::move(cb), std::move(props), make_config); +} + +void Permissions::delete_permission(std::shared_ptr user, + Permission permission, + PermissionChangeCallback callback, + const ConfigMaker& make_config) +{ + permission.access = AccessLevel::None; + set_permission(std::move(user), std::move(permission), std::move(callback), make_config); +} + +void Permissions::make_offer(std::shared_ptr user, + PermissionOffer offer, + PermissionOfferCallback callback, + const ConfigMaker& make_config) +{ + auto props = AnyDict{ + {"expiresAt", std::move(offer.expiration)}, + {"userId", user->identity()}, + {"realmUrl", user->server_url() + offer.path}, + {"mayRead", offer.access != AccessLevel::None}, + {"mayWrite", offer.access == AccessLevel::Write || offer.access == AccessLevel::Admin}, + {"mayManage", offer.access == AccessLevel::Admin}, + }; + perform_async_operation("PermissionOffer", + std::move(user), + make_handler_extracting_property("token", std::move(callback)), + std::move(props), + make_config); +} + +void Permissions::accept_offer(std::shared_ptr user, + const std::string& token, + PermissionOfferCallback callback, + const ConfigMaker& make_config) +{ + perform_async_operation("PermissionOfferResponse", + std::move(user), + make_handler_extracting_property("realmUrl", std::move(callback)), + AnyDict{ {"token", token} }, + make_config); +} + +void Permissions::perform_async_operation(const std::string& object_type, + std::shared_ptr user, + AsyncOperationHandler handler, + AnyDict additional_props, + const ConfigMaker& make_config) +{; + auto realm = Permissions::management_realm(std::move(user), make_config); + CppContext context; + + // Get the current time. + int64_t ns_since_epoch = ns_since_unix_epoch(system_clock::now()); + int64_t s_arg = ns_since_epoch / (int64_t)Timestamp::nanoseconds_per_second; + int32_t ns_arg = ns_since_epoch % Timestamp::nanoseconds_per_second; + + auto props = AnyDict{ + {"id", util::uuid_string()}, + {"createdAt", Timestamp(s_arg, ns_arg)}, + {"updatedAt", Timestamp(s_arg, ns_arg)}, + }; + props.insert(additional_props.begin(), additional_props.end()); + + // Write the permission object. + realm->begin_transaction(); + auto raw = Object::create(context, realm, *realm->schema().find(object_type), std::move(props), false); + auto object = std::make_shared<_impl::NotificationWrapper>(std::move(raw)); + realm->commit_transaction(); + + // Observe the permission object until the permission change has been processed or failed. + // The notifier is automatically unregistered upon the completion of the permission + // change, one way or another. + auto block = [object, handler=std::move(handler)](CollectionChangeSet, std::exception_ptr ex) mutable { + if (ex) { + handler(nullptr, ex); + object.reset(); + return; + } + + CppContext context; + auto status_code = object->get_property_value(context, "statusCode"); + if (!status_code.has_value()) { + // Continue waiting for the sync server to complete the operation. + return; + } + + // Determine whether an error happened or not. + if (auto code = any_cast(status_code)) { + // The permission change failed because an error was returned from the server. + auto status = object->get_property_value(context, "statusMessage"); + std::string error_str = (status.has_value() + ? any_cast(status) + : util::format("Error code: %1", code)); + handler(nullptr, std::make_exception_ptr(PermissionActionException(error_str, code))); + } + else { + handler(object.get(), nullptr); + } + object.reset(); + }; + object->add_notification_callback(std::move(block)); +} + +SharedRealm Permissions::management_realm(std::shared_ptr user, const ConfigMaker& make_config) +{ + // FIXME: maybe we should cache the management Realm on the user, so we don't need to open it every time. + const auto realm_url = util::format("realm%1/~/__management", user->server_url().substr(4)); + Realm::Config config = make_config(user, std::move(realm_url)); + config.sync_config->stop_policy = SyncSessionStopPolicy::Immediately; + config.schema = Schema{ + {"PermissionChange", { + Property{"id", PropertyType::String, Property::IsPrimary{true}}, + Property{"createdAt", PropertyType::Date}, + Property{"updatedAt", PropertyType::Date}, + Property{"statusCode", PropertyType::Int|PropertyType::Nullable}, + Property{"statusMessage", PropertyType::String|PropertyType::Nullable}, + Property{"userId", PropertyType::String}, + Property{"metadataKey", PropertyType::String|PropertyType::Nullable}, + Property{"metadataValue", PropertyType::String|PropertyType::Nullable}, + Property{"metadataNameSpace", PropertyType::String|PropertyType::Nullable}, + Property{"realmUrl", PropertyType::String}, + Property{"mayRead", PropertyType::Bool|PropertyType::Nullable}, + Property{"mayWrite", PropertyType::Bool|PropertyType::Nullable}, + Property{"mayManage", PropertyType::Bool|PropertyType::Nullable}, + }}, + {"PermissionOffer", { + Property{"id", PropertyType::String, Property::IsPrimary{true}}, + Property{"createdAt", PropertyType::Date}, + Property{"updatedAt", PropertyType::Date}, + Property{"expiresAt", PropertyType::Date|PropertyType::Nullable}, + Property{"statusCode", PropertyType::Int|PropertyType::Nullable}, + Property{"statusMessage", PropertyType::String|PropertyType::Nullable}, + Property{"token", PropertyType::String|PropertyType::Nullable}, + Property{"realmUrl", PropertyType::String}, + Property{"mayRead", PropertyType::Bool}, + Property{"mayWrite", PropertyType::Bool}, + Property{"mayManage", PropertyType::Bool}, + }}, + {"PermissionOfferResponse", { + Property{"id", PropertyType::String, Property::IsPrimary{true}}, + Property{"createdAt", PropertyType::Date}, + Property{"updatedAt", PropertyType::Date}, + Property{"statusCode", PropertyType::Int|PropertyType::Nullable}, + Property{"statusMessage", PropertyType::String|PropertyType::Nullable}, + Property{"token", PropertyType::String}, + Property{"realmUrl", PropertyType::String|PropertyType::Nullable}, + }}, + }; + config.schema_version = 0; + auto shared_realm = Realm::get_shared_realm(std::move(config)); + user->register_management_session(shared_realm->config().path); + return shared_realm; +} + +SharedRealm Permissions::permission_realm(std::shared_ptr user, const ConfigMaker& make_config) +{ + // FIXME: maybe we should cache the permission Realm on the user, so we don't need to open it every time. + const auto realm_url = util::format("realm%1/~/__permission", user->server_url().substr(4)); + Realm::Config config = make_config(user, std::move(realm_url)); + config.sync_config->stop_policy = SyncSessionStopPolicy::Immediately; + config.schema = Schema{ + {"Permission", { + {"updatedAt", PropertyType::Date}, + {"userId", PropertyType::String}, + {"path", PropertyType::String}, + {"mayRead", PropertyType::Bool}, + {"mayWrite", PropertyType::Bool}, + {"mayManage", PropertyType::Bool}, + }} + }; + config.schema_version = 0; + auto shared_realm = Realm::get_shared_realm(std::move(config)); + user->register_permission_session(shared_realm->config().path); + return shared_realm; +} diff --git a/Pods/Realm/Realm/ObjectStore/src/sync/sync_session.cpp b/Pods/Realm/Realm/ObjectStore/src/sync/sync_session.cpp index 9ff0df1..e3bddb2 100644 --- a/Pods/Realm/Realm/ObjectStore/src/sync/sync_session.cpp +++ b/Pods/Realm/Realm/ObjectStore/src/sync/sync_session.cpp @@ -27,11 +27,12 @@ #include #include - using namespace realm; using namespace realm::_impl; using namespace realm::_impl::sync_session_states; +using SessionWaiterPointer = void(sync::Session::*)(std::function); + constexpr const char SyncError::c_original_file_path_key[]; constexpr const char SyncError::c_recovery_file_path_key[]; @@ -48,8 +49,6 @@ constexpr const char SyncError::c_recovery_file_path_key[]; /// * ACTIVE: when the binding successfully refreshes the token /// * INACTIVE: if asked to log out, or if asked to close and the stop policy /// is Immediate. -/// * DYING: if asked to close and the stop policy is AfterChangesUploaded -/// * ERROR: if a fatal error occurs /// /// ACTIVE: the session is connected to the Realm Object Server and is actively /// transferring data. @@ -60,7 +59,6 @@ constexpr const char SyncError::c_recovery_file_path_key[]; /// * INACTIVE: if asked to log out, or if asked to close and the stop policy /// is Immediate. /// * DYING: if asked to close and the stop policy is AfterChangesUploaded -/// * ERROR: if a fatal error occurs /// /// DYING: the session is performing clean-up work in preparation to be destroyed. /// From: ACTIVE @@ -69,51 +67,61 @@ constexpr const char SyncError::c_recovery_file_path_key[]; /// revived, or if explicitly asked to log out before the /// clean-up work begins /// * ACTIVE: if the session is revived -/// * ERROR: if a fatal error occurs /// /// INACTIVE: the user owning this session has logged out, the `sync::Session` /// owned by this session is destroyed, and the session is quiescent. +/// Note that a session briefly enters this state before being destroyed, but +/// it can also enter this state and stay there if the user has been logged out. /// From: initial, WAITING_FOR_ACCESS_TOKEN, ACTIVE, DYING /// To: /// * WAITING_FOR_ACCESS_TOKEN: if the session is revived -/// * ERROR: if a fatal error occurs -/// -/// ERROR: a non-recoverable error has occurred, and this session is semantically -/// invalid. The binding must create a new session with a different configuration. -/// From: WAITING_FOR_ACCESS_TOKEN, ACTIVE, DYING, INACTIVE -/// To: -/// * (none, this is a terminal state) /// struct SyncSession::State { virtual ~State() { } + // Move the given session into this state. All state transitions MUST be carried out through this method. virtual void enter_state(std::unique_lock&, SyncSession&) const { } virtual void refresh_access_token(std::unique_lock&, SyncSession&, std::string, const util::Optional&) const { } - virtual void bind_with_admin_token(std::unique_lock&, - SyncSession&, const std::string&, const std::string&) const { } - - /// Returns true iff the lock is still locked when the method returns. + // Returns true iff the lock is still locked when the method returns. virtual bool access_token_expired(std::unique_lock&, SyncSession&) const { return true; } - virtual void nonsync_transact_notify(std::unique_lock&, SyncSession&, sync::Session::version_type) const { } + virtual void nonsync_transact_notify(std::unique_lock&, SyncSession&, sync::version_type) const { } + // Perform any work needed to reactivate a session that is not already active. + // Returns true iff the session should ask the binding to get a token for `bind()`. virtual bool revive_if_needed(std::unique_lock&, SyncSession&) const { return false; } - virtual void log_out(std::unique_lock&, SyncSession&) const { } + // Perform any work needed to respond to the application regaining network connectivity. + virtual void handle_reconnect(std::unique_lock&, SyncSession&) const { }; - virtual void close_if_connecting(std::unique_lock&, SyncSession&) const { } + // The user that owns this session has been logged out, and the session should take appropriate action. + virtual void log_out(std::unique_lock&, SyncSession&) const { } + // The session should be closed and moved to `inactive`, in accordance with its stop policy and other state. virtual void close(std::unique_lock&, SyncSession&) const { } + // Returns true iff the error has been fully handled and the error handler should immediately return. + virtual bool handle_error(std::unique_lock&, SyncSession&, const SyncError&) const { return false; } + + // Register a handler to wait for sync session uploads, downloads, or synchronization. + // PRECONDITION: the session state lock must be held at the time this method is called, until after it returns. + // Returns true iff the handler was registered, either immediately or placed in a queue for later registration. + virtual bool wait_for_completion(SyncSession&, + std::function, + SessionWaiterPointer) const { + return false; + } + + virtual void override_server(std::unique_lock&, SyncSession&, std::string, int) const { } + static const State& waiting_for_access_token; static const State& active; static const State& dying; static const State& inactive; - static const State& error; }; struct sync_session_states::WaitingForAccessToken : public SyncSession::State { @@ -126,6 +134,8 @@ struct sync_session_states::WaitingForAccessToken : public SyncSession::State { std::string access_token, const util::Optional& server_url) const override { + session.create_sync_session(); + // Since the sync session was previously unbound, it's safe to do this from the // calling thread. if (!session.m_server_url) { @@ -133,17 +143,29 @@ struct sync_session_states::WaitingForAccessToken : public SyncSession::State { } if (session.m_session_has_been_bound) { session.m_session->refresh(std::move(access_token)); + session.m_session->cancel_reconnect_delay(); } else { session.m_session->bind(*session.m_server_url, std::move(access_token)); session.m_session_has_been_bound = true; } + + if (session.m_server_override) + session.m_session->override_server(session.m_server_override->address, session.m_server_override->port); + + // Register all the pending wait-for-completion blocks. + for (auto& package : session.m_completion_wait_packages) { + (*session.m_session.*package.waiter)(std::move(package.callback)); + } + session.m_completion_wait_packages.clear(); + + // Handle any deferred commit notification. if (session.m_deferred_commit_notification) { session.m_session->nonsync_transact_notify(*session.m_deferred_commit_notification); session.m_deferred_commit_notification = util::none; } + session.advance_state(lock, active); if (session.m_deferred_close) { - session.m_deferred_close = false; session.m_state->close(lock, session); } } @@ -153,23 +175,55 @@ struct sync_session_states::WaitingForAccessToken : public SyncSession::State { session.advance_state(lock, inactive); } + bool revive_if_needed(std::unique_lock&, SyncSession& session) const override + { + session.m_deferred_close = false; + return false; + } + + void handle_reconnect(std::unique_lock& lock, SyncSession& session) const override + { + // Ask the binding to retry getting the token for this session. + std::shared_ptr session_ptr = session.shared_from_this(); + lock.unlock(); + session.m_config.bind_session_handler(session_ptr->m_realm_path, session_ptr->m_config, session_ptr); + } + void nonsync_transact_notify(std::unique_lock&, SyncSession& session, - sync::Session::version_type version) const override + sync::version_type version) const override { // Notify at first available opportunity. session.m_deferred_commit_notification = version; } - void close_if_connecting(std::unique_lock& lock, SyncSession& session) const override + void close(std::unique_lock& lock, SyncSession& session) const override { - // Ignore the sync configuration's stop policy as we're not yet connected. - session.advance_state(lock, inactive); + switch (session.m_config.stop_policy) { + case SyncSessionStopPolicy::Immediately: + // Immediately kill the session. + session.advance_state(lock, inactive); + break; + case SyncSessionStopPolicy::LiveIndefinitely: + case SyncSessionStopPolicy::AfterChangesUploaded: + // Defer handling closing the session until after the login response succeeds. + session.m_deferred_close = true; + break; + } } - void close(std::unique_lock&, SyncSession& session) const override + bool wait_for_completion(SyncSession& session, + std::function callback, + SessionWaiterPointer waiter) const override { - session.m_deferred_close = true; + session.m_completion_wait_packages.push_back({ waiter, std::move(callback) }); + return true; + } + + void override_server(std::unique_lock&, SyncSession& session, + std::string address, int port) const override + { + session.m_server_override = SyncSession::ServerOverride{address, port}; } }; @@ -179,6 +233,10 @@ struct sync_session_states::Active : public SyncSession::State { const util::Optional&) const override { session.m_session->refresh(std::move(access_token)); + // Cancel the session's reconnection delay. This is important if the + // token is being refreshed as a response to a 202 (token expired) + // error, or similar non-fatal sync errors. + session.m_session->cancel_reconnect_delay(); } bool access_token_expired(std::unique_lock& lock, SyncSession& session) const override @@ -196,7 +254,7 @@ struct sync_session_states::Active : public SyncSession::State { } void nonsync_transact_notify(std::unique_lock&, SyncSession& session, - sync::Session::version_type version) const override + sync::version_type version) const override { // Fully ready sync session, notify immediately. session.m_session->nonsync_transact_notify(version); @@ -217,20 +275,63 @@ struct sync_session_states::Active : public SyncSession::State { break; } } + + bool wait_for_completion(SyncSession& session, + std::function callback, + SessionWaiterPointer waiter) const override + { + REALM_ASSERT(session.m_session); + (*session.m_session.*waiter)(std::move(callback)); + return true; + } + + void handle_reconnect(std::unique_lock&, SyncSession& session) const override + { + session.m_session->cancel_reconnect_delay(); + } + + void override_server(std::unique_lock&, SyncSession& session, + std::string address, int port) const override + { + session.m_server_override = SyncSession::ServerOverride{address, port}; + session.m_session->override_server(address, port); + } }; struct sync_session_states::Dying : public SyncSession::State { - void enter_state(std::unique_lock&, SyncSession& session) const override + void enter_state(std::unique_lock& lock, SyncSession& session) const override { + // If we have no session, we cannot possibly upload anything. + if (!session.m_session) { + session.advance_state(lock, inactive); + return; + } + size_t current_death_count = ++session.m_death_count; - session.m_session->async_wait_for_upload_completion([session=&session, current_death_count](std::error_code) { - std::unique_lock lock(session->m_state_mutex); - if (session->m_state == &State::dying && session->m_death_count == current_death_count) { - session->advance_state(lock, inactive); + std::weak_ptr weak_session = session.shared_from_this(); + session.m_session->async_wait_for_upload_completion([weak_session, current_death_count](std::error_code) { + if (auto session = weak_session.lock()) { + std::unique_lock lock(session->m_state_mutex); + if (session->m_state == &State::dying && session->m_death_count == current_death_count) { + session->advance_state(lock, inactive); + } } }); } + bool handle_error(std::unique_lock& lock, SyncSession& session, const SyncError& error) const override + { + if (error.is_fatal) { + session.advance_state(lock, inactive); + } + // If the error isn't fatal, don't change state, but don't + // allow it to be reported either. + // FIXME: What if the token expires while a session is dying? + // Should we allow the token to be refreshed so that changes + // can finish being uploaded? + return true; + } + bool revive_if_needed(std::unique_lock& lock, SyncSession& session) const override { // Revive. @@ -242,42 +343,56 @@ struct sync_session_states::Dying : public SyncSession::State { { session.advance_state(lock, inactive); } + + bool wait_for_completion(SyncSession& session, + std::function callback, + SessionWaiterPointer waiter) const override + { + REALM_ASSERT(session.m_session); + (*session.m_session.*waiter)(std::move(callback)); + return true; + } + + void override_server(std::unique_lock&, SyncSession& session, + std::string address, int port) const override + { + session.m_server_override = SyncSession::ServerOverride{address, port}; + session.m_session->override_server(address, port); + } }; struct sync_session_states::Inactive : public SyncSession::State { void enter_state(std::unique_lock& lock, SyncSession& session) const override { + auto completion_wait_packages = std::move(session.m_completion_wait_packages); + session.m_completion_wait_packages.clear(); session.m_session = nullptr; - session.m_server_url = util::none; - session.unregister(lock); - } + session.unregister(lock); // releases lock - void bind_with_admin_token(std::unique_lock& lock, SyncSession& session, - const std::string& admin_token, - const std::string& server_url) const override - { - session.create_sync_session(); - session.advance_state(lock, waiting_for_access_token); - session.m_state->refresh_access_token(lock, session, admin_token, server_url); + // Inform any queued-up completion handlers that they were cancelled. + for (auto& package : completion_wait_packages) + package.callback(util::error::operation_aborted); } bool revive_if_needed(std::unique_lock& lock, SyncSession& session) const override { - // Revive. - session.create_sync_session(); session.advance_state(lock, waiting_for_access_token); return true; } -}; -struct sync_session_states::Error : public SyncSession::State { - void enter_state(std::unique_lock&, SyncSession& session) const override + bool wait_for_completion(SyncSession& session, + std::function callback, + SessionWaiterPointer waiter) const override { - session.m_session = nullptr; - session.m_config = { nullptr, "", SyncSessionStopPolicy::Immediately, nullptr }; + session.m_completion_wait_packages.push_back({ waiter, std::move(callback) }); + return true; } - // Everything else is a no-op when in the error state. + void override_server(std::unique_lock&, SyncSession& session, + std::string address, int port) const override + { + session.m_server_override = SyncSession::ServerOverride{address, port}; + } }; @@ -285,14 +400,36 @@ const SyncSession::State& SyncSession::State::waiting_for_access_token = Waiting const SyncSession::State& SyncSession::State::active = Active(); const SyncSession::State& SyncSession::State::dying = Dying(); const SyncSession::State& SyncSession::State::inactive = Inactive(); -const SyncSession::State& SyncSession::State::error = Error(); - SyncSession::SyncSession(SyncClient& client, std::string realm_path, SyncConfig config) : m_state(&State::inactive) , m_config(std::move(config)) , m_realm_path(std::move(realm_path)) -, m_client(client) { } +, m_client(client) +{ + // Sync history validation ensures that the history within the Realm file is in a format that can be used + // by the version of realm-sync that we're using. Validation is enabled by default when the binding manually + // opens a sync session (via `SyncManager::get_session`), but is disabled when the sync session is opened + // as a side effect of opening a `Realm`. In that case, the sync history has already been validated by the + // act of opening the `Realm` so it's not necessary to repeat it here. + if (m_config.validate_sync_history) { + Realm::Config realm_config; + realm_config.path = m_realm_path; + realm_config.schema_mode = SchemaMode::Additive; + realm_config.force_sync_history = true; + realm_config.cache = false; + + if (m_config.realm_encryption_key) { + realm_config.encryption_key.resize(64); + std::copy(m_config.realm_encryption_key->begin(), m_config.realm_encryption_key->end(), + realm_config.encryption_key.begin()); + } + + // FIXME: Opening a Realm only to discard it is relatively expensive. It may be preferable to have + // realm-sync open the Realm when the `sync::Session` is created since it can continue to use it. + Realm::get_shared_realm(realm_config); // Throws + } +} std::string SyncSession::get_recovery_file_path() { @@ -300,12 +437,42 @@ std::string SyncSession::get_recovery_file_path() util::create_timestamped_template("recovered_realm")); } +void SyncSession::update_error_and_mark_file_for_deletion(SyncError& error, ShouldBackup should_backup) +{ + // Add a SyncFileActionMetadata marking the Realm as needing to be deleted. + std::string recovery_path; + auto original_path = path(); + error.user_info[SyncError::c_original_file_path_key] = original_path; + if (should_backup == ShouldBackup::yes) { + recovery_path = get_recovery_file_path(); + error.user_info[SyncError::c_recovery_file_path_key] = recovery_path; + } + using Action = SyncFileActionMetadata::Action; + auto action = should_backup == ShouldBackup::yes ? Action::BackUpThenDeleteRealm : Action::DeleteRealm; + SyncManager::shared().perform_metadata_update([this, + action, + original_path=std::move(original_path), + recovery_path=std::move(recovery_path)](const auto& manager) { + manager.make_file_action_metadata(original_path, m_config.realm_url(), m_config.user->identity(), + action, std::move(recovery_path)); + }); +} + // This method should only be called from within the error handler callback registered upon the underlying `m_session`. void SyncSession::handle_error(SyncError error) { - bool should_invalidate_session = error.is_fatal; + enum class NextStateAfterError { none, inactive, error }; + auto next_state = error.is_fatal ? NextStateAfterError::error : NextStateAfterError::none; auto error_code = error.error_code; + { + // See if the current state wishes to take responsibility for handling the error. + std::unique_lock lock(m_state_mutex); + if (m_state->handle_error(lock, *this, error)) { + return; + } + } + if (error_code.category() == realm::sync::protocol_error_category()) { using ProtocolError = realm::sync::ProtocolError; switch (static_cast(error_code.value())) { @@ -322,6 +489,16 @@ void SyncSession::handle_error(SyncError error) case ProtocolError::reuse_of_session_ident: case ProtocolError::bound_in_other_session: case ProtocolError::bad_message_order: + case ProtocolError::bad_client_version: + case ProtocolError::illegal_realm_path: + case ProtocolError::no_such_realm: + case ProtocolError::bad_changeset: + case ProtocolError::bad_changeset_header_syntax: + case ProtocolError::bad_changeset_size: + case ProtocolError::bad_changesets: + case ProtocolError::bad_decompression: + case ProtocolError::partial_sync_disabled: + case ProtocolError::unsupported_session_feature: break; // Session errors case ProtocolError::session_closed: @@ -339,57 +516,44 @@ void SyncSession::handle_error(SyncError error) } case ProtocolError::bad_authentication: { std::shared_ptr user_to_invalidate; - should_invalidate_session = false; + next_state = NextStateAfterError::none; { std::unique_lock lock(m_state_mutex); user_to_invalidate = user(); - advance_state(lock, State::error); + cancel_pending_waits(lock); } if (user_to_invalidate) user_to_invalidate->invalidate(); break; } - case ProtocolError::illegal_realm_path: - case ProtocolError::no_such_realm: - case ProtocolError::permission_denied: - case ProtocolError::bad_client_version: + case ProtocolError::permission_denied: { + next_state = NextStateAfterError::inactive; + update_error_and_mark_file_for_deletion(error, ShouldBackup::no); break; + } + case ProtocolError::bad_origin_file_ident: case ProtocolError::bad_server_file_ident: case ProtocolError::bad_client_file_ident: case ProtocolError::bad_server_version: - case ProtocolError::diverging_histories: { - // Add a SyncFileActionMetadata marking the Realm as needing to be deleted. - auto recovery_path = get_recovery_file_path(); - auto original_path = path(); - error.user_info[SyncError::c_original_file_path_key] = original_path; - error.user_info[SyncError::c_recovery_file_path_key] = recovery_path; - SyncManager::shared().perform_metadata_update([this, - original_path=std::move(original_path), - recovery_path=std::move(recovery_path)](const auto& manager) { - SyncFileActionMetadata(manager, - SyncFileActionMetadata::Action::HandleRealmForClientReset, - original_path, - m_config.realm_url, - m_config.user->identity(), - util::Optional(std::move(recovery_path))); - }); - break; - } - case ProtocolError::bad_changeset: + case ProtocolError::diverging_histories: + next_state = NextStateAfterError::inactive; + update_error_and_mark_file_for_deletion(error, ShouldBackup::yes); break; } } else if (error_code.category() == realm::sync::client_error_category()) { using ClientError = realm::sync::Client::Error; switch (static_cast(error_code.value())) { case ClientError::connection_closed: + case ClientError::pong_timeout: // Not real errors, don't need to be reported to the binding. return; + case ClientError::bad_timestamp: + case ClientError::connect_timeout: case ClientError::unknown_message: case ClientError::bad_syntax: case ClientError::limits_exceeded: case ClientError::bad_session_ident: case ClientError::bad_message_order: - case ClientError::bad_file_ident_pair: case ClientError::bad_progress: case ClientError::bad_changeset_header_syntax: case ClientError::bad_changeset_size: @@ -399,81 +563,80 @@ void SyncSession::handle_error(SyncError error) case ClientError::bad_request_ident: case ClientError::bad_error_code: case ClientError::bad_compression: + case ClientError::bad_client_version: + case ClientError::ssl_server_cert_rejected: + case ClientError::bad_file_ident: + case ClientError::bad_client_file_ident: + case ClientError::bad_client_file_ident_salt: // Don't do anything special for these errors. // Future functionality may require special-case handling for existing // errors, or newly introduced error codes. break; } } else { - // Unrecognized error code; just ignore it. - return; - } - if (should_invalidate_session) { - std::unique_lock lock(m_state_mutex); - advance_state(lock, State::error); + // Unrecognized error code. + error.is_unrecognized_by_client = true; + } + switch (next_state) { + case NextStateAfterError::none: + break; + case NextStateAfterError::inactive: { + std::unique_lock lock(m_state_mutex); + advance_state(lock, State::inactive); + break; + } + case NextStateAfterError::error: { + std::unique_lock lock(m_state_mutex); + cancel_pending_waits(lock); + break; + } } - if (m_error_handler) { - m_error_handler(shared_from_this(), std::move(error)); + if (m_config.error_handler) { + m_config.error_handler(shared_from_this(), std::move(error)); } } -void SyncSession::handle_progress_update(uint64_t downloaded, uint64_t downloadable, - uint64_t uploaded, uint64_t uploadable) +void SyncSession::cancel_pending_waits(std::unique_lock& lock) { - std::vector> invocations; - { - std::lock_guard lock(m_progress_notifier_mutex); - m_current_progress = Progress{uploadable, downloadable, uploaded, downloaded}; - - for (auto it = m_notifiers.begin(); it != m_notifiers.end();) { - auto& package = it->second; - package.update(*m_current_progress); - - bool should_delete = false; - invocations.emplace_back(package.create_invocation(*m_current_progress, should_delete)); + auto packages = std::move(m_completion_wait_packages); + lock.unlock(); - it = (should_delete ? m_notifiers.erase(it) : std::next(it)); - } - } - // Run the notifiers only after we've released the lock. - for (auto& invocation : invocations) { - invocation(); + // Inform any queued-up completion handlers that they were cancelled. + for (auto& package : packages) { + package.callback(util::error::operation_aborted); } } -void SyncSession::NotifierPackage::update(const Progress& current_progress) +void SyncSession::handle_progress_update(uint64_t downloaded, uint64_t downloadable, + uint64_t uploaded, uint64_t uploadable, + uint64_t download_version, uint64_t snapshot_version) { - if (is_streaming || captured_transferrable) - return; - - captured_transferrable = direction == NotifierType::download ? current_progress.downloadable - : current_progress.uploadable; + m_notifier.update(downloaded, downloadable, uploaded, uploadable, download_version, snapshot_version); } -std::function SyncSession::NotifierPackage::create_invocation(const Progress& current_progress, bool& is_expired) const +void SyncSession::create_sync_session() { - REALM_ASSERT(is_streaming || captured_transferrable); + if (m_session) + return; - bool is_download = direction == NotifierType::download; - uint64_t transferred = is_download ? current_progress.downloaded : current_progress.uploaded; - uint64_t transferrable; - if (is_streaming) { - transferrable = is_download ? current_progress.downloadable : current_progress.uploadable; - } else { - transferrable = *captured_transferrable; + sync::Session::Config session_config; + session_config.changeset_cooker = m_config.transformer; + session_config.encryption_key = m_config.realm_encryption_key; + session_config.verify_servers_ssl_certificate = m_config.client_validate_ssl; + session_config.ssl_trust_certificate_path = m_config.ssl_trust_certificate_path; + session_config.ssl_verify_callback = m_config.ssl_verify_callback; + session_config.multiplex_ident = m_multiplex_identity; + + if (m_config.authorization_header_name) { + session_config.authorization_header_name = *m_config.authorization_header_name; } - // A notifier is expired if at least as many bytes have been transferred - // as were originally considered transferrable. - is_expired = !is_streaming && transferred >= *captured_transferrable; - return [=, package=*this](){ - package.notifier(transferred, transferrable); - }; -} + session_config.custom_http_headers = m_config.custom_http_headers; -void SyncSession::create_sync_session() -{ - REALM_ASSERT(!m_session); - m_session = std::make_unique(m_client.client, m_realm_path); + if (m_config.url_prefix) { + session_config.url_prefix = *m_config.url_prefix; + } + + m_session = m_client.make_session(m_realm_path, std::move(session_config)); // The next time we get a token, call `bind()` instead of `refresh()`. m_session_has_been_bound = false; @@ -502,13 +665,14 @@ void SyncSession::create_sync_session() m_session->set_sync_transact_callback(std::move(wrapped_callback)); // Set up the wrapped progress handler callback - auto wrapped_progress_handler = [this, weak_self](uint_fast64_t downloaded, uint_fast64_t downloadable, - uint_fast64_t uploaded, uint_fast64_t uploadable) { + m_session->set_progress_handler([weak_self](uint_fast64_t downloaded, uint_fast64_t downloadable, + uint_fast64_t uploaded, uint_fast64_t uploadable, + uint_fast64_t progress_version, uint_fast64_t snapshot_version) { if (auto self = weak_self.lock()) { - handle_progress_update(downloaded, downloadable, uploaded, uploadable); + self->handle_progress_update(downloaded, downloadable, uploaded, + uploadable, progress_version, snapshot_version); } - }; - m_session->set_progress_handler(std::move(wrapped_progress_handler)); + }); } void SyncSession::set_sync_transact_callback(std::function callback) @@ -516,11 +680,6 @@ void SyncSession::set_sync_transact_callback(std::function handler) -{ - m_error_handler = std::move(handler); -} - void SyncSession::advance_state(std::unique_lock& lock, const State& state) { REALM_ASSERT(lock.owns_lock()); @@ -529,43 +688,42 @@ void SyncSession::advance_state(std::unique_lock& lock, const State& m_state->enter_state(lock, *this); } -void SyncSession::nonsync_transact_notify(sync::Session::version_type version) +void SyncSession::nonsync_transact_notify(sync::version_type version) { + m_notifier.set_local_version(version); + std::unique_lock lock(m_state_mutex); m_state->nonsync_transact_notify(lock, *this, version); } -void SyncSession::revive_if_needed(std::shared_ptr session) +void SyncSession::revive_if_needed() { - REALM_ASSERT(session); util::Optional&> handler; { - std::unique_lock lock(session->m_state_mutex); - if (session->m_state->revive_if_needed(lock, *session)) { - handler = session->m_config.bind_session_handler; - } - } - if (handler) { - handler.value()(session->m_realm_path, session->m_config, session); + std::unique_lock lock(m_state_mutex); + if (m_state->revive_if_needed(lock, *this)) + handler = m_config.bind_session_handler; } + if (handler) + handler.value()(m_realm_path, m_config, shared_from_this()); } -void SyncSession::log_out() +void SyncSession::handle_reconnect() { std::unique_lock lock(m_state_mutex); - m_state->log_out(lock, *this); + m_state->handle_reconnect(lock, *this); } -void SyncSession::close() +void SyncSession::log_out() { std::unique_lock lock(m_state_mutex); - m_state->close(lock, *this); + m_state->log_out(lock, *this); } -void SyncSession::close_if_connecting() +void SyncSession::close() { std::unique_lock lock(m_state_mutex); - m_state->close_if_connecting(lock, *this); + m_state->close(lock, *this); } void SyncSession::unregister(std::unique_lock& lock) @@ -577,110 +735,203 @@ void SyncSession::unregister(std::unique_lock& lock) SyncManager::shared().unregister_session(m_realm_path); } -bool SyncSession::can_wait_for_network_completion() const +bool SyncSession::wait_for_upload_completion(std::function callback) { - return m_state == &State::active || m_state == &State::dying; + std::unique_lock lock(m_state_mutex); + return m_state->wait_for_completion(*this, std::move(callback), &sync::Session::async_wait_for_upload_completion); } -bool SyncSession::wait_for_upload_completion(std::function callback) +bool SyncSession::wait_for_download_completion(std::function callback) { std::unique_lock lock(m_state_mutex); - // FIXME: instead of dropping the callback if we haven't yet `bind()`ed, - // save it and register it when the session `bind()`s. - if (can_wait_for_network_completion()) { - REALM_ASSERT(m_session); - m_session->async_wait_for_upload_completion(std::move(callback)); - return true; - } - return false; + return m_state->wait_for_completion(*this, std::move(callback), &sync::Session::async_wait_for_download_completion); } -bool SyncSession::wait_for_download_completion(std::function callback) +uint64_t SyncSession::register_progress_notifier(std::function notifier, + NotifierType direction, bool is_streaming) +{ + return m_notifier.register_callback(std::move(notifier), direction, is_streaming); +} + +void SyncSession::unregister_progress_notifier(uint64_t token) +{ + m_notifier.unregister_callback(token); +} + +void SyncSession::refresh_access_token(std::string access_token, util::Optional server_url) { std::unique_lock lock(m_state_mutex); - // FIXME: instead of dropping the callback if we haven't yet `bind()`ed, - // save it and register it when the session `bind()`s. - if (can_wait_for_network_completion()) { - REALM_ASSERT(m_session); - m_session->async_wait_for_download_completion(std::move(callback)); - return true; + if (!m_server_url && !server_url) { + // The first time this method is called, the server URL must be provided. + return; } - return false; + m_state->refresh_access_token(lock, *this, std::move(access_token), server_url); } -bool SyncSession::wait_for_upload_completion_blocking() +void SyncSession::override_server(std::string address, int port) { std::unique_lock lock(m_state_mutex); - if (can_wait_for_network_completion()) { - REALM_ASSERT(m_session); - m_session->wait_for_upload_complete_or_client_stopped(); - return true; + m_state->override_server(lock, *this, std::move(address), port); +} + +void SyncSession::set_multiplex_identifier(std::string multiplex_identity) +{ + m_multiplex_identity = std::move(multiplex_identity); +} + +SyncSession::PublicState SyncSession::state() const +{ + std::unique_lock lock(m_state_mutex); + if (m_state == &State::waiting_for_access_token) { + return PublicState::WaitingForAccessToken; + } else if (m_state == &State::active) { + return PublicState::Active; + } else if (m_state == &State::dying) { + return PublicState::Dying; + } else if (m_state == &State::inactive) { + return PublicState::Inactive; } - return false; + REALM_UNREACHABLE(); } -uint64_t SyncSession::register_progress_notifier(std::function notifier, +// Represents a reference to the SyncSession from outside of the sync subsystem. +// We attempt to keep the SyncSession in an active state as long as it has an external reference. +class SyncSession::ExternalReference { +public: + ExternalReference(std::shared_ptr session) : m_session(std::move(session)) + {} + + ~ExternalReference() + { + m_session->did_drop_external_reference(); + } + +private: + std::shared_ptr m_session; +}; + +std::shared_ptr SyncSession::external_reference() +{ + std::unique_lock lock(m_state_mutex); + + if (auto external_reference = m_external_reference.lock()) + return std::shared_ptr(external_reference, this); + + auto external_reference = std::make_shared(shared_from_this()); + m_external_reference = external_reference; + return std::shared_ptr(external_reference, this); +} + +std::shared_ptr SyncSession::existing_external_reference() +{ + std::unique_lock lock(m_state_mutex); + + if (auto external_reference = m_external_reference.lock()) + return std::shared_ptr(external_reference, this); + + return nullptr; +} + +void SyncSession::did_drop_external_reference() +{ + std::unique_lock lock(m_state_mutex); + + // If the session is being resurrected we should not close the session. + if (!m_external_reference.expired()) + return; + + m_state->close(lock, *this); +} + +uint64_t SyncProgressNotifier::register_callback(std::function notifier, NotifierType direction, bool is_streaming) { std::function invocation; uint64_t token_value = 0; { - std::lock_guard lock(m_progress_notifier_mutex); + std::lock_guard lock(m_mutex); token_value = m_progress_notifier_token++; - NotifierPackage package{std::move(notifier), is_streaming, direction}; + NotifierPackage package{std::move(notifier), util::none, m_local_transaction_version, + is_streaming, direction == NotifierType::download}; if (!m_current_progress) { // Simply register the package, since we have no data yet. - m_notifiers.emplace(token_value, std::move(package)); + m_packages.emplace(token_value, std::move(package)); return token_value; } - package.update(*m_current_progress); bool skip_registration = false; invocation = package.create_invocation(*m_current_progress, skip_registration); if (skip_registration) { token_value = 0; } else { - m_notifiers.emplace(token_value, std::move(package)); + m_packages.emplace(token_value, std::move(package)); } } invocation(); return token_value; } -void SyncSession::unregister_progress_notifier(uint64_t token) +void SyncProgressNotifier::unregister_callback(uint64_t token) { - std::lock_guard lock(m_progress_notifier_mutex); - m_notifiers.erase(token); + std::lock_guard lock(m_mutex); + m_packages.erase(token); } -void SyncSession::refresh_access_token(std::string access_token, util::Optional server_url) +void SyncProgressNotifier::update(uint64_t downloaded, uint64_t downloadable, + uint64_t uploaded, uint64_t uploadable, + uint64_t download_version, uint64_t snapshot_version) { - std::unique_lock lock(m_state_mutex); - if (!m_server_url && !server_url) { - // The first time this method is called, the server URL must be provided. + // Ignore progress messages from before we first receive a DOWNLOAD message + if (download_version == 0) return; + + std::vector> invocations; + { + std::lock_guard lock(m_mutex); + m_current_progress = Progress{uploadable, downloadable, uploaded, downloaded, snapshot_version}; + + for (auto it = m_packages.begin(); it != m_packages.end(); ) { + bool should_delete = false; + invocations.emplace_back(it->second.create_invocation(*m_current_progress, should_delete)); + it = should_delete ? m_packages.erase(it) : std::next(it); + } } - m_state->refresh_access_token(lock, *this, std::move(access_token), server_url); + // Run the notifiers only after we've released the lock. + for (auto& invocation : invocations) + invocation(); } -void SyncSession::bind_with_admin_token(std::string admin_token, std::string server_url) +void SyncProgressNotifier::set_local_version(uint64_t snapshot_version) { - std::unique_lock lock(m_state_mutex); - m_state->bind_with_admin_token(lock, *this, admin_token, server_url); + std::lock_guard lock(m_mutex); + m_local_transaction_version = snapshot_version; } -SyncSession::PublicState SyncSession::state() const +std::function SyncProgressNotifier::NotifierPackage::create_invocation(Progress const& current_progress, bool& is_expired) { - std::unique_lock lock(m_state_mutex); - if (m_state == &State::waiting_for_access_token) { - return PublicState::WaitingForAccessToken; - } else if (m_state == &State::active) { - return PublicState::Active; - } else if (m_state == &State::dying) { - return PublicState::Dying; - } else if (m_state == &State::inactive) { - return PublicState::Inactive; - } else if (m_state == &State::error) { - return PublicState::Error; + uint64_t transferrable; + if (is_streaming) { + transferrable = is_download ? current_progress.downloadable : current_progress.uploadable; } - REALM_UNREACHABLE(); + else if (captured_transferrable) { + transferrable = *captured_transferrable; + } + else { + if (is_download) + captured_transferrable = current_progress.downloadable; + else { + // If the sync client has not yet processed all of the local + // transactions then the uploadable data is incorrect and we should + // not invoke the callback + if (snapshot_version > current_progress.snapshot_version) + return []{}; + captured_transferrable = current_progress.uploadable; + } + transferrable = *captured_transferrable; + } + + uint64_t transferred = is_download ? current_progress.downloaded : current_progress.uploaded; + // A notifier is expired if at least as many bytes have been transferred + // as were originally considered transferrable. + is_expired = !is_streaming && transferred >= transferrable; + return [=, notifier=notifier] { notifier(transferred, transferrable); }; } diff --git a/Pods/Realm/Realm/ObjectStore/src/sync/sync_user.cpp b/Pods/Realm/Realm/ObjectStore/src/sync/sync_user.cpp index c5d8c71..1adf8b1 100644 --- a/Pods/Realm/Realm/ObjectStore/src/sync/sync_user.cpp +++ b/Pods/Realm/Realm/ObjectStore/src/sync/sync_user.cpp @@ -24,21 +24,40 @@ namespace realm { +SyncUserContextFactory SyncUser::s_binding_context_factory; +std::mutex SyncUser::s_binding_context_factory_mutex; + SyncUser::SyncUser(std::string refresh_token, std::string identity, util::Optional server_url, - bool is_admin) + util::Optional local_identity, + TokenType token_type) : m_state(State::Active) , m_server_url(server_url.value_or("")) -, m_is_admin(is_admin) +, m_token_type(token_type) , m_refresh_token(std::move(refresh_token)) , m_identity(std::move(identity)) { - if (!is_admin) { - SyncManager::shared().perform_metadata_update([this, server_url=std::move(server_url)](const auto& manager) { - auto metadata = SyncUserMetadata(manager, m_identity); - metadata.set_state(server_url, m_refresh_token); + { + std::lock_guard lock(s_binding_context_factory_mutex); + if (s_binding_context_factory) { + m_binding_context = s_binding_context_factory(); + } + } + if (token_type == TokenType::Normal) { + REALM_ASSERT(m_server_url.length() > 0); + bool updated = SyncManager::shared().perform_metadata_update([=](const auto& manager) { + auto metadata = manager.get_or_make_user_metadata(m_identity, m_server_url); + metadata->set_user_token(m_refresh_token); + m_is_admin = metadata->is_admin(); + m_local_identity = metadata->local_uuid(); }); + if (!updated) + m_local_identity = m_identity; + } else { + // Admin token users. The local identity serves as the directory path. + REALM_ASSERT(local_identity); + m_local_identity = std::move(*local_identity); } } @@ -86,6 +105,12 @@ void SyncUser::update_refresh_token(std::string token) std::vector> sessions_to_revive; { std::unique_lock lock(m_mutex); + if (auto session = m_management_session.lock()) + sessions_to_revive.emplace_back(std::move(session)); + + if (auto session = m_permission_session.lock()) + sessions_to_revive.emplace_back(std::move(session)); + switch (m_state) { case State::Error: return; @@ -107,10 +132,10 @@ void SyncUser::update_refresh_token(std::string token) } } // Update persistent user metadata. - if (!m_is_admin) { + if (m_token_type != TokenType::Admin) { SyncManager::shared().perform_metadata_update([=](const auto& manager) { - auto metadata = SyncUserMetadata(manager, m_identity); - metadata.set_state(m_server_url, token); + auto metadata = manager.get_or_make_user_metadata(m_identity, m_server_url); + metadata->set_user_token(token); }); } } @@ -118,14 +143,14 @@ void SyncUser::update_refresh_token(std::string token) // Note that we do this after releasing the lock, since the session may // need to access protected User state in the process of binding itself. for (auto& session : sessions_to_revive) { - SyncSession::revive_if_needed(session); + session->revive_if_needed(); } } void SyncUser::log_out() { - if (m_is_admin) { - // Admin users cannot be logged out. + if (m_token_type == TokenType::Admin) { + // Admin-token users cannot be logged out. return; } std::lock_guard lock(m_mutex); @@ -142,13 +167,31 @@ void SyncUser::log_out() } } m_sessions.clear(); + // Deactivate the sessions for the management and admin Realms. + if (auto session = m_management_session.lock()) + session->log_out(); + + if (auto session = m_permission_session.lock()) + session->log_out(); + // Mark the user as 'dead' in the persisted metadata Realm. - if (!m_is_admin) { - SyncManager::shared().perform_metadata_update([=](const auto& manager) { - auto metadata = SyncUserMetadata(manager, m_identity, false); - metadata.mark_for_removal(); - }); + SyncManager::shared().perform_metadata_update([=](const auto& manager) { + auto metadata = manager.get_or_make_user_metadata(m_identity, m_server_url, false); + if (metadata) + metadata->mark_for_removal(); + }); +} + +void SyncUser::set_is_admin(bool is_admin) +{ + if (m_token_type == TokenType::Admin) { + return; } + m_is_admin = is_admin; + SyncManager::shared().perform_metadata_update([=](const auto& manager) { + auto metadata = manager.get_or_make_user_metadata(m_identity, m_server_url); + metadata->set_is_admin(is_admin); + }); } void SyncUser::invalidate() @@ -177,12 +220,8 @@ void SyncUser::register_session(std::shared_ptr session) case State::Active: // Immediately ask the session to come online. m_sessions[path] = session; - if (m_is_admin) { - session->bind_with_admin_token(m_refresh_token, session->config().realm_url); - } else { - lock.unlock(); - SyncSession::revive_if_needed(std::move(session)); - } + lock.unlock(); + session->revive_if_needed(); break; case State::LoggedOut: m_waiting_sessions[path] = session; @@ -192,4 +231,35 @@ void SyncUser::register_session(std::shared_ptr session) } } +void SyncUser::set_binding_context_factory(SyncUserContextFactory factory) +{ + std::lock_guard lock(s_binding_context_factory_mutex); + s_binding_context_factory = std::move(factory); +} + +void SyncUser::register_management_session(const std::string& path) +{ + std::lock_guard lock(m_mutex); + if (m_management_session.lock() || m_state == State::Error) + return; + + m_management_session = SyncManager::shared().get_existing_session(path); +} + +void SyncUser::register_permission_session(const std::string& path) +{ + std::lock_guard lock(m_mutex); + if (m_permission_session.lock() || m_state == State::Error) + return; + + m_permission_session = SyncManager::shared().get_existing_session(path); +} + +} + +namespace std { +size_t hash::operator()(const realm::SyncUserIdentifier& k) const +{ + return ((hash()(k.user_id) ^ (hash()(k.auth_server_url) << 1)) >> 1); +} } diff --git a/Pods/Realm/Realm/ObjectStore/src/thread_safe_reference.cpp b/Pods/Realm/Realm/ObjectStore/src/thread_safe_reference.cpp index 68b0eb5..9469d89 100644 --- a/Pods/Realm/Realm/ObjectStore/src/thread_safe_reference.cpp +++ b/Pods/Realm/Realm/ObjectStore/src/thread_safe_reference.cpp @@ -55,14 +55,14 @@ V ThreadSafeReferenceBase::invalidate_after_import(Realm& destination_realm, T c REALM_ASSERT_DEBUG(!m_source_realm->is_in_transaction()); REALM_ASSERT_DEBUG(!is_invalidated()); - SharedGroup& destination_shared_group = Realm::Internal::get_shared_group(destination_realm); + SharedGroup& destination_shared_group = *Realm::Internal::get_shared_group(destination_realm); auto unpin_version = util::make_scope_exit([&]() noexcept { invalidate(); }); return construct_with_shared_group(destination_shared_group); } SharedGroup& ThreadSafeReferenceBase::get_source_shared_group() const { - return Realm::Internal::get_shared_group(*m_source_realm); + return *Realm::Internal::get_shared_group(*m_source_realm); } bool ThreadSafeReferenceBase::has_same_config(Realm& realm) const { @@ -72,24 +72,27 @@ bool ThreadSafeReferenceBase::has_same_config(Realm& realm) const { void ThreadSafeReferenceBase::invalidate() { REALM_ASSERT_DEBUG(m_source_realm); SharedRealm thread_local_realm = Realm::Internal::get_coordinator(*m_source_realm).get_realm(); - Realm::Internal::get_shared_group(*thread_local_realm).unpin_version(m_version_id); + Realm::Internal::get_shared_group(*thread_local_realm)->unpin_version(m_version_id); m_source_realm = nullptr; } ThreadSafeReference::ThreadSafeReference(List const& list) : ThreadSafeReferenceBase(list.get_realm()) -, m_link_view(get_source_shared_group().export_linkview_for_handover(list.m_link_view)) { } +, m_link_view(get_source_shared_group().export_linkview_for_handover(list.m_link_view)) +, m_table(get_source_shared_group().export_table_for_handover(list.m_table)) +{ } List ThreadSafeReference::import_into_realm(SharedRealm realm) && { return invalidate_after_import(*realm, [&](SharedGroup& shared_group) { - LinkViewRef link_view = shared_group.import_linkview_from_handover(std::move(m_link_view)); - return List(std::move(realm), std::move(link_view)); + if (auto link_view = shared_group.import_linkview_from_handover(std::move(m_link_view))) + return List(std::move(realm), std::move(link_view)); + return List(std::move(realm), shared_group.import_table_from_handover(std::move(m_table))); }); } ThreadSafeReference::ThreadSafeReference(Object const& object) : ThreadSafeReferenceBase(object.realm()) -, m_row(get_source_shared_group().export_for_handover(object.row())) +, m_row(get_source_shared_group().export_for_handover(Row(object.row()))) , m_object_schema_name(object.get_object_schema().name) { } Object ThreadSafeReference::import_into_realm(SharedRealm realm) && { @@ -104,23 +107,17 @@ Object ThreadSafeReference::import_into_realm(SharedRealm realm) && { ThreadSafeReference::ThreadSafeReference(Results const& results) : ThreadSafeReferenceBase(results.get_realm()) , m_query(get_source_shared_group().export_for_handover(results.get_query(), ConstSourcePayload::Copy)) -, m_sort_order([&]() { - SortDescriptor::HandoverPatch sort_order; - SortDescriptor::generate_patch(results.get_sort(), sort_order); - return sort_order; -}()) -, m_distinct_descriptor([&]() { - SortDescriptor::HandoverPatch distinct_descriptor; - SortDescriptor::generate_patch(results.get_distinct(), distinct_descriptor); - return distinct_descriptor; +, m_ordering_patch([&]() { + DescriptorOrdering::HandoverPatch ordering_patch; + DescriptorOrdering::generate_patch(results.get_descriptor_ordering(), ordering_patch); + return ordering_patch; }()){ } Results ThreadSafeReference::import_into_realm(SharedRealm realm) && { return invalidate_after_import(*realm, [&](SharedGroup& shared_group) { Query query = *shared_group.import_from_handover(std::move(m_query)); Table& table = *query.get_table(); - SortDescriptor sort_descriptor = SortDescriptor::create_from_and_consume_patch(m_sort_order, table); - SortDescriptor distinct_descriptor = SortDescriptor::create_from_and_consume_patch(m_distinct_descriptor, table); - return Results(std::move(realm), std::move(query), std::move(sort_descriptor), std::move(distinct_descriptor)); + DescriptorOrdering descriptors = DescriptorOrdering::create_from_and_consume_patch(m_ordering_patch, table); + return Results(std::move(realm), std::move(query), std::move(descriptors)); }); } diff --git a/Pods/Realm/Realm/ObjectStore/src/util/format.cpp b/Pods/Realm/Realm/ObjectStore/src/util/format.cpp deleted file mode 100644 index 4103c8d..0000000 --- a/Pods/Realm/Realm/ObjectStore/src/util/format.cpp +++ /dev/null @@ -1,82 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#include "util/format.hpp" - -#include - -#include -#include - -namespace realm { namespace _impl { -Printable::Printable(StringData value) : m_type(Type::String), m_string(value.data()) { } - -void Printable::print(std::ostream& out) const -{ - switch (m_type) { - case Printable::Type::Bool: - out << (m_uint ? "true" : "false"); - break; - case Printable::Type::Uint: - out << m_uint; - break; - case Printable::Type::Int: - out << m_int; - break; - case Printable::Type::String: - out << m_string; - break; - } -} - -std::string format(const char* fmt, std::initializer_list values) -{ - std::stringstream ss; - while (*fmt) { - auto next = strchr(fmt, '%'); - - // emit the rest of the format string if there are no more percents - if (!next) { - ss << fmt; - break; - } - - // emit everything up to the next percent - ss.write(fmt, next - fmt); - ++next; - REALM_ASSERT(*next); - - // %% produces a single escaped % - if (*next == '%') { - ss << '%'; - fmt = next + 1; - continue; - } - REALM_ASSERT(isdigit(*next)); - - // The const_cast is safe because stroul does not actually modify - // the pointed-to string, but it lacks a const overload - auto index = strtoul(next, const_cast(&fmt), 10) - 1; - REALM_ASSERT(index < values.size()); - (values.begin() + index)->print(ss); - } - return ss.str(); -} - -} // namespace _impl -} // namespace realm diff --git a/Pods/Realm/Realm/ObjectStore/src/util/uuid.cpp b/Pods/Realm/Realm/ObjectStore/src/util/uuid.cpp new file mode 100644 index 0000000..0110002 --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/util/uuid.cpp @@ -0,0 +1,80 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2017 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "util/uuid.hpp" + +#include +#include +#include +#include +#include + +namespace { + +// Seed `engine` with as much random state as it requires, based on the approach outlined in P0205R0. +// +template +T create_and_seed_engine() +{ + constexpr auto bytes_needed = T::state_size * sizeof(typename T::result_type); + + constexpr auto numbers_needed = sizeof(std::random_device::result_type) < sizeof(std::seed_seq::result_type) + ? (bytes_needed / sizeof(std::random_device::result_type)) + : (bytes_needed / sizeof(std::seed_seq::result_type)); + + std::array state; + std::random_device rd; + std::generate(begin(state), end(state), std::ref(rd)); + std::seed_seq seeds(begin(state), end(state)); + + T engine; + engine.seed(seeds); + return engine; +} + +} // unnamed namespace + +namespace realm { +namespace util { + +std::string uuid_string() +{ + static auto engine = create_and_seed_engine(); + + std::array uuid_bytes; + std::uniform_int_distribution distribution(0, std::numeric_limits::max()); + std::generate(begin(uuid_bytes), end(uuid_bytes), [&] { return distribution(engine); }); + + // Version 4 UUID. + uuid_bytes[6] = (uuid_bytes[6] & 0x0f) | 0x40; + // IETF variant. + uuid_bytes[8] = (uuid_bytes[8] & 0x3f) | 0x80; + + std::array uuid_formatted; + snprintf(uuid_formatted.data(), uuid_formatted.size(), + "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + uuid_bytes[0], uuid_bytes[1], uuid_bytes[2], uuid_bytes[3], + uuid_bytes[4], uuid_bytes[5], uuid_bytes[6], uuid_bytes[7], + uuid_bytes[8], uuid_bytes[9], uuid_bytes[10], uuid_bytes[11], + uuid_bytes[12], uuid_bytes[13], uuid_bytes[14], uuid_bytes[15]); + + return std::string(uuid_formatted.data(), uuid_formatted.size() - 1); +} + +} // namespace util +} // namespace realm diff --git a/Pods/Realm/Realm/RLMAccessor.mm b/Pods/Realm/Realm/RLMAccessor.mm index c8aa40d..4b1fa64 100644 --- a/Pods/Realm/Realm/RLMAccessor.mm +++ b/Pods/Realm/Realm/RLMAccessor.mm @@ -16,7 +16,7 @@ // //////////////////////////////////////////////////////////////////////////// -#import "RLMAccessor.h" +#import "RLMAccessor.hpp" #import "RLMArray_Private.hpp" #import "RLMListBase.h" @@ -26,358 +26,268 @@ #import "RLMObservation.hpp" #import "RLMProperty_Private.h" #import "RLMRealm_Private.hpp" -#import "RLMResults_Private.h" +#import "RLMResults_Private.hpp" #import "RLMSchema_Private.h" #import "RLMUtil.hpp" #import "results.hpp" #import "property.hpp" #import +#import #import +#pragma mark - Helper functions + +namespace { template -static inline T get(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index) { +T get(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index) { RLMVerifyAttached(obj); - return obj->_row.get_table()->get(obj->_info->objectSchema->persisted_properties[index].table_column, - obj->_row.get_index()); + return obj->_row.get(obj->_info->objectSchema->persisted_properties[index].table_column); } template -static NSNumber *getBoxed(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index) { +id getBoxed(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index) { RLMVerifyAttached(obj); - auto col = obj->_info->objectSchema->persisted_properties[index].table_column; + auto& prop = obj->_info->objectSchema->persisted_properties[index]; + auto col = prop.table_column; if (obj->_row.is_null(col)) { return nil; } - return @(obj->_row.get_table()->get(col, obj->_row.get_index())); -} - -// long getter/setter -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, long long val, bool setDefault) { - RLMVerifyInWriteTransaction(obj); - obj->_row.get_table()->set_int(colIndex, obj->_row.get_index(), val, setDefault); -} -// float getter/setter -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, float val, bool setDefault) { - RLMVerifyInWriteTransaction(obj); - obj->_row.get_table()->set_float(colIndex, obj->_row.get_index(), val, setDefault); -} - -// double getter/setter -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, double val, bool setDefault) { - RLMVerifyInWriteTransaction(obj); - obj->_row.get_table()->set_double(colIndex, obj->_row.get_index(), val, setDefault); + RLMAccessorContext ctx(obj, &prop); + return ctx.box(obj->_row.get(col)); } -// bool getter/setter -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, BOOL val, bool setDefault) { +template +void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, T val) { RLMVerifyInWriteTransaction(obj); - obj->_row.get_table()->set_bool(colIndex, obj->_row.get_index(), val, setDefault); + obj->_row.set(colIndex, val); } -// string getter/setter -static inline NSString *RLMGetString(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) { - return RLMStringDataToNSString(get(obj, colIndex)); -} -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, __unsafe_unretained NSString *const val, bool setDefault) { - RLMVerifyInWriteTransaction(obj); +template +void translateError(Fn&& fn) { try { - obj->_row.get_table()->set_string(colIndex, obj->_row.get_index(), RLMStringDataWithNSString(val), setDefault); + fn(); } catch (std::exception const& e) { @throw RLMException(e); } } -// date getter/setter -static inline NSDate *RLMGetDate(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) { - return RLMTimestampToNSDate(get(obj, colIndex)); -} -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, __unsafe_unretained NSDate *const date, bool setDefault) { +void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained NSString *const val) { RLMVerifyInWriteTransaction(obj); - if (date) { - obj->_row.get_table()->set_timestamp(colIndex, obj->_row.get_index(), RLMTimestampForNSDate(date), setDefault); - } - else { - obj->_row.set_null(colIndex); - } + translateError([&] { + obj->_row.set(colIndex, RLMStringDataWithNSString(val)); + }); } -// data getter/setter -static inline NSData *RLMGetData(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) { - return RLMBinaryDataToNSData(get(obj, colIndex)); -} -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, __unsafe_unretained NSData *const data, bool setDefault) { - RLMVerifyInWriteTransaction(obj); - - try { - obj->_row.get_table()->set_binary(colIndex, obj->_row.get_index(), RLMBinaryDataForNSData(data), setDefault); - } - catch (std::exception const& e) { - @throw RLMException(e); - } +[[gnu::noinline]] +void setNull(realm::Row& row, size_t col) { + translateError([&] { row.set_null(col); }); } -static inline RLMObjectBase *RLMGetLinkedObjectForValue(__unsafe_unretained RLMRealm *const realm, - __unsafe_unretained NSString *const className, - __unsafe_unretained id const value, - RLMCreationOptions creationOptions) NS_RETURNS_RETAINED; -static inline RLMObjectBase *RLMGetLinkedObjectForValue(__unsafe_unretained RLMRealm *const realm, - __unsafe_unretained NSString *const className, - __unsafe_unretained id const value, - RLMCreationOptions creationOptions) { - RLMObjectBase *link = RLMDynamicCast(value); - if (!link || ![link->_objectSchema.className isEqualToString:className]) { - // create from non-rlmobject - return RLMCreateObjectInRealmWithValue(realm, className, value, creationOptions & RLMCreationOptionsCreateOrUpdate); - } - - if (link.isInvalidated) { - @throw RLMException(@"Adding a deleted or invalidated object to a Realm is not permitted"); - } - - if (link->_realm == realm) { - return link; +void setValue(__unsafe_unretained RLMObjectBase *const obj, + NSUInteger colIndex, __unsafe_unretained NSDate *const date) { + RLMVerifyInWriteTransaction(obj); + if (date) { + obj->_row.set(colIndex, RLMTimestampForNSDate(date)); } - - if (creationOptions & RLMCreationOptionsPromoteUnmanaged) { - if (!link->_realm) { - RLMAddObjectToRealm(link, realm, creationOptions & RLMCreationOptionsCreateOrUpdate); - return link; - } - @throw RLMException(@"Can not add objects from a different Realm"); + else { + setNull(obj->_row, colIndex); } - - // copy from another realm or copy from unmanaged - return RLMCreateObjectInRealmWithValue(realm, className, link, creationOptions & RLMCreationOptionsCreateOrUpdate); } -// link getter/setter -static inline RLMObjectBase *RLMGetLink(__unsafe_unretained RLMObjectBase *const obj, NSUInteger propertyIndex) { - RLMVerifyAttached(obj); - auto colIndex = obj->_info->objectSchema->persisted_properties[propertyIndex].table_column; - - if (obj->_row.is_null_link(colIndex)) { - return nil; - } - NSUInteger index = obj->_row.get_link(colIndex); - return RLMCreateObjectAccessor(obj->_realm, obj->_info->linkTargetType(propertyIndex), index); +void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained NSData *const data) { + RLMVerifyInWriteTransaction(obj); + translateError([&] { + obj->_row.set(colIndex, RLMBinaryDataForNSData(data)); + }); } -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, - __unsafe_unretained RLMObjectBase *const val, bool setDefault) { - RLMVerifyInWriteTransaction(obj); +void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained RLMObjectBase *const val) { if (!val) { + RLMVerifyInWriteTransaction(obj); obj->_row.nullify_link(colIndex); return; } - RLMObjectBase *link = RLMGetLinkedObjectForValue(obj->_realm, val->_objectSchema.className, - val, RLMCreationOptionsPromoteUnmanaged); + RLMAddObjectToRealm(val, obj->_realm, false); // make sure it is the correct type - if (link->_row.get_table() != obj->_row.get_table()->get_link_target(colIndex)) { + if (val->_row.get_table() != obj->_row.get_table()->get_link_target(colIndex)) { @throw RLMException(@"Can't set object of type '%@' to property of type '%@'", val->_objectSchema.className, obj->_info->propertyForTableColumn(colIndex).objectClassName); } - obj->_row.get_table()->set_link(colIndex, obj->_row.get_index(), link->_row.get_index(), setDefault); + obj->_row.set_link(colIndex, val->_row.get_index()); } // array getter/setter -static inline RLMArray *RLMGetArray(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) { +RLMArray *getArray(__unsafe_unretained RLMObjectBase *const obj, NSUInteger propIndex) { RLMVerifyAttached(obj); - auto prop = obj->_info->rlmObjectSchema.properties[colIndex]; - return [[RLMArrayLinkView alloc] initWithParent:obj property:prop]; + auto prop = obj->_info->rlmObjectSchema.properties[propIndex]; + return [[RLMManagedArray alloc] initWithParent:obj property:prop]; } -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, - __unsafe_unretained id const array, __unused bool setDefault) { +void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained id const value) { RLMVerifyInWriteTransaction(obj); + auto prop = obj->_info->propertyForTableColumn(colIndex); + RLMValidateValueForProperty(value, obj->_info->rlmObjectSchema, prop, true); - realm::LinkViewRef linkView = obj->_row.get_linklist(colIndex); - // remove all old - // FIXME: make sure delete rules don't purge objects - linkView->clear(); - for (RLMObjectBase *link in array) { - RLMObjectBase * addedLink = RLMGetLinkedObjectForValue(obj->_realm, link->_objectSchema.className, link, RLMCreationOptionsPromoteUnmanaged); - linkView->add(addedLink->_row.get_index()); + realm::List list(obj->_realm->_realm, *obj->_row.get_table(), colIndex, obj->_row.get_index()); + RLMClassInfo *info = obj->_info; + if (list.get_type() == realm::PropertyType::Object) { + info = &obj->_info->linkTargetType(prop.index); } + RLMAccessorContext ctx(obj->_realm, *info); + translateError([&] { list.assign(ctx, value, false); }); } -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, - __unsafe_unretained NSNumber *const intObject, bool setDefault) { +void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained NSNumber *const intObject) { RLMVerifyInWriteTransaction(obj); if (intObject) { - obj->_row.get_table()->set_int(colIndex, obj->_row.get_index(), intObject.longLongValue, setDefault); + obj->_row.set(colIndex, intObject.longLongValue); } else { - obj->_row.get_table()->set_null(colIndex, obj->_row.get_index(), setDefault); + setNull(obj->_row, colIndex); } } -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, - __unsafe_unretained NSNumber *const floatObject, bool setDefault) { +void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained NSNumber *const floatObject) { RLMVerifyInWriteTransaction(obj); if (floatObject) { - obj->_row.get_table()->set_float(colIndex, obj->_row.get_index(), floatObject.floatValue, setDefault); + obj->_row.set(colIndex, floatObject.floatValue); } else { - obj->_row.get_table()->set_null(colIndex, obj->_row.get_index(), setDefault); + setNull(obj->_row, colIndex); } } -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, - __unsafe_unretained NSNumber *const doubleObject, bool setDefault) { +void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained NSNumber *const doubleObject) { RLMVerifyInWriteTransaction(obj); if (doubleObject) { - obj->_row.get_table()->set_double(colIndex, obj->_row.get_index(), doubleObject.doubleValue, setDefault); + obj->_row.set(colIndex, doubleObject.doubleValue); } else { - obj->_row.get_table()->set_null(colIndex, obj->_row.get_index(), setDefault); + setNull(obj->_row, colIndex); } } -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, - __unsafe_unretained NSNumber *const boolObject, bool setDefault) { +void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained NSNumber *const boolObject) { RLMVerifyInWriteTransaction(obj); if (boolObject) { - obj->_row.get_table()->set_bool(colIndex, obj->_row.get_index(), boolObject.boolValue, setDefault); + obj->_row.set(colIndex, (bool)boolObject.boolValue); } else { - obj->_row.get_table()->set_null(colIndex, obj->_row.get_index(), setDefault); + setNull(obj->_row, colIndex); } } -static inline RLMLinkingObjects *RLMGetLinkingObjects(__unsafe_unretained RLMObjectBase *const obj, - __unsafe_unretained RLMProperty *const property) { +RLMLinkingObjects *getLinkingObjects(__unsafe_unretained RLMObjectBase *const obj, + __unsafe_unretained RLMProperty *const property) { + RLMVerifyAttached(obj); auto& objectInfo = obj->_realm->_info[property.objectClassName]; - auto linkingProperty = objectInfo.objectSchema->property_for_name(property.linkOriginPropertyName.UTF8String); - auto backlinkView = obj->_row.get_table()->get_backlink_view(obj->_row.get_index(), objectInfo.table(), linkingProperty->table_column); + auto& linkOrigin = obj->_info->objectSchema->computed_properties[property.index].link_origin_property_name; + auto linkingProperty = objectInfo.objectSchema->property_for_name(linkOrigin); + auto backlinkView = obj->_row.get_table()->get_backlink_view(obj->_row.get_index(), + objectInfo.table(), + linkingProperty->table_column); realm::Results results(obj->_realm->_realm, std::move(backlinkView)); return [RLMLinkingObjects resultsWithObjectInfo:objectInfo results:std::move(results)]; } // any getter/setter -static inline id RLMGetAnyProperty(__unsafe_unretained RLMObjectBase *const obj, NSUInteger col_ndx) { - RLMVerifyAttached(obj); - return RLMMixedToObjc(obj->_row.get_mixed(col_ndx)); +template +id makeGetter(NSUInteger index) { + return ^(__unsafe_unretained RLMObjectBase *const obj) { + return static_cast(get(obj, index)); + }; } -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger, __unsafe_unretained id, bool) { - RLMVerifyInWriteTransaction(obj); - @throw RLMException(@"Modifying Mixed properties is not supported"); + +template +id makeBoxedGetter(NSUInteger index) { + return ^(__unsafe_unretained RLMObjectBase *const obj) { + return getBoxed(obj, index); + }; +} +template +id makeOptionalGetter(NSUInteger index) { + return ^(__unsafe_unretained RLMObjectBase *const obj) { + return getBoxed>(obj, index); + }; +} +template +id makeNumberGetter(NSUInteger index, bool boxed, bool optional) { + if (optional) { + return makeOptionalGetter(index); + } + if (boxed) { + return makeBoxedGetter(index); + } + return makeGetter(index); } // dynamic getter with column closure -static id RLMAccessorGetter(RLMProperty *prop, const char *type) { +id managedGetter(RLMProperty *prop, const char *type) { NSUInteger index = prop.index; - bool boxed = prop.optional || *type == '@'; + if (prop.array && prop.type != RLMPropertyTypeLinkingObjects) { + return ^id(__unsafe_unretained RLMObjectBase *const obj) { + return getArray(obj, index); + }; + } + + bool boxed = *type == '@'; switch (prop.type) { case RLMPropertyTypeInt: - if (boxed) { - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return getBoxed(obj, index); - }; + if (prop.optional || boxed) { + return makeNumberGetter(index, boxed, prop.optional); } switch (*type) { - case 'c': - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return static_cast(get(obj, index)); - }; - case 's': - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return static_cast(get(obj, index)); - }; - case 'i': - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return static_cast(get(obj, index)); - }; - case 'l': - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return static_cast(get(obj, index)); - }; - case 'q': - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return static_cast(get(obj, index)); - }; + case 'c': return makeGetter(index); + case 's': return makeGetter(index); + case 'i': return makeGetter(index); + case 'l': return makeGetter(index); + case 'q': return makeGetter(index); default: @throw RLMException(@"Unexpected property type for Objective-C type code"); } case RLMPropertyTypeFloat: - if (boxed) { - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return getBoxed(obj, index); - }; - } - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return get(obj, index); - }; + return makeNumberGetter(index, boxed, prop.optional); case RLMPropertyTypeDouble: - if (boxed) { - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return getBoxed(obj, index); - }; - } - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return get(obj, index); - }; + return makeNumberGetter(index, boxed, prop.optional); case RLMPropertyTypeBool: - if (boxed) { - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return getBoxed(obj, index); - }; - } - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return get(obj, index); - }; + return makeNumberGetter(index, boxed, prop.optional); case RLMPropertyTypeString: - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return RLMGetString(obj, index); - }; + return makeBoxedGetter(index); case RLMPropertyTypeDate: - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return RLMGetDate(obj, index); - }; + return makeBoxedGetter(index); case RLMPropertyTypeData: - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return RLMGetData(obj, index); - }; + return makeBoxedGetter(index); case RLMPropertyTypeObject: - return ^id(__unsafe_unretained RLMObjectBase *const obj) { - return RLMGetLink(obj, index); - }; - case RLMPropertyTypeArray: - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return RLMGetArray(obj, index); - }; + return makeBoxedGetter(index); case RLMPropertyTypeAny: @throw RLMException(@"Cannot create accessor class for schema with Mixed properties"); case RLMPropertyTypeLinkingObjects: return ^(__unsafe_unretained RLMObjectBase *const obj) { - return RLMGetLinkingObjects(obj, prop); + return getLinkingObjects(obj, prop); }; } } -template -static void RLMWrapSetter(__unsafe_unretained RLMObjectBase *const obj, __unsafe_unretained NSString *const name, Function&& f) { - if (RLMObservationInfo *info = RLMGetObservationInfo(obj->_observationInfo, obj->_row.get_index(), *obj->_info)) { - info->willChange(name); - f(); - info->didChange(name); - } - else { - f(); - } -} - template -static id makeSetter(__unsafe_unretained RLMProperty *const prop) { +id makeSetter(__unsafe_unretained RLMProperty *const prop) { NSUInteger index = prop.index; NSString *name = prop.name; if (prop.isPrimary) { @@ -385,16 +295,30 @@ static id makeSetter(__unsafe_unretained RLMProperty *const prop) { @throw RLMException(@"Primary key can't be changed after an object is inserted."); }; } + return ^(__unsafe_unretained RLMObjectBase *const obj, ArgType val) { - RLMWrapSetter(obj, name, [&] { - RLMSetValue(obj, obj->_info->objectSchema->persisted_properties[index].table_column, - static_cast(val), false); - }); + auto set = [&] { + setValue(obj, obj->_info->objectSchema->persisted_properties[index].table_column, + static_cast(val)); + }; + if (RLMObservationInfo *info = RLMGetObservationInfo(obj->_observationInfo, + obj->_row.get_index(), *obj->_info)) { + info->willChange(name); + set(); + info->didChange(name); + } + else { + set(); + } }; } // dynamic setter with column closure -static id RLMAccessorSetter(RLMProperty *prop, const char *type) { +id managedSetter(RLMProperty *prop, const char *type) { + if (prop.array && prop.type != RLMPropertyTypeLinkingObjects) { + return makeSetter>(prop); + } + bool boxed = prop.optional || *type == '@'; switch (prop.type) { case RLMPropertyTypeInt: @@ -415,19 +339,18 @@ static id RLMAccessorSetter(RLMProperty *prop, const char *type) { case RLMPropertyTypeDouble: return boxed ? makeSetter *>(prop) : makeSetter(prop); case RLMPropertyTypeBool: - return boxed ? makeSetter *>(prop) : makeSetter(prop); + return boxed ? makeSetter *>(prop) : makeSetter(prop); case RLMPropertyTypeString: return makeSetter(prop); case RLMPropertyTypeDate: return makeSetter(prop); case RLMPropertyTypeData: return makeSetter(prop); - case RLMPropertyTypeObject: return makeSetter(prop); - case RLMPropertyTypeArray: return makeSetter(prop); - case RLMPropertyTypeAny: return makeSetter(prop); + case RLMPropertyTypeAny: return nil; case RLMPropertyTypeLinkingObjects: return nil; + case RLMPropertyTypeObject: return makeSetter(prop); } } // call getter for superclass for property at colIndex -static id RLMSuperGet(RLMObjectBase *obj, NSString *propName) { +id superGet(RLMObjectBase *obj, NSString *propName) { typedef id (*getter_type)(RLMObjectBase *, SEL); RLMProperty *prop = obj->_objectSchema[propName]; Class superClass = class_getSuperclass(obj.class); @@ -436,7 +359,7 @@ static id RLMSuperGet(RLMObjectBase *obj, NSString *propName) { } // call setter for superclass for property at colIndex -static void RLMSuperSet(RLMObjectBase *obj, NSString *propName, id val) { +void superSet(RLMObjectBase *obj, NSString *propName, id val) { typedef void (*setter_type)(RLMObjectBase *, SEL, RLMArray *ar); RLMProperty *prop = obj->_objectSchema[propName]; Class superClass = class_getSuperclass(obj.class); @@ -445,84 +368,63 @@ static void RLMSuperSet(RLMObjectBase *obj, NSString *propName, id val) { } // getter/setter for unmanaged object -static id RLMAccessorUnmanagedGetter(RLMProperty *prop, const char *) { +id unmanagedGetter(RLMProperty *prop, const char *) { // only override getters for RLMArray and linking objects properties - if (prop.type == RLMPropertyTypeArray) { - NSString *objectClassName = prop.objectClassName; + if (prop.type == RLMPropertyTypeLinkingObjects) { + return ^(RLMObjectBase *) { return [RLMResults emptyDetachedResults]; }; + } + if (prop.array) { NSString *propName = prop.name; - + if (prop.type == RLMPropertyTypeObject) { + NSString *objectClassName = prop.objectClassName; + return ^(RLMObjectBase *obj) { + id val = superGet(obj, propName); + if (!val) { + val = [[RLMArray alloc] initWithObjectClassName:objectClassName]; + superSet(obj, propName, val); + } + return val; + }; + } + auto type = prop.type; + auto optional = prop.optional; return ^(RLMObjectBase *obj) { - id val = RLMSuperGet(obj, propName); + id val = superGet(obj, propName); if (!val) { - val = [[RLMArray alloc] initWithObjectClassName:objectClassName]; - RLMSuperSet(obj, propName, val); + val = [[RLMArray alloc] initWithObjectType:type optional:optional]; + superSet(obj, propName, val); } return val; }; } - else if (prop.type == RLMPropertyTypeLinkingObjects) { - return ^(RLMObjectBase *){ - return [RLMResults emptyDetachedResults]; - }; - } return nil; } -static id RLMAccessorUnmanagedSetter(RLMProperty *prop, const char *) { - if (prop.type != RLMPropertyTypeArray) { + +id unmanagedSetter(RLMProperty *prop, const char *) { + // Only RLMArray needs special handling for the unmanaged setter + if (!prop.array) { return nil; } NSString *propName = prop.name; - NSString *objectClassName = prop.objectClassName; - return ^(RLMObjectBase *obj, id ar) { + return ^(RLMObjectBase *obj, id values) { + auto prop = obj->_objectSchema[propName]; + RLMValidateValueForProperty(values, obj->_objectSchema, prop, true); + // make copy when setting (as is the case for all other variants) - RLMArray *standaloneAr = [[RLMArray alloc] initWithObjectClassName:objectClassName]; - [standaloneAr addObjects:ar]; - RLMSuperSet(obj, propName, standaloneAr); + RLMArray *ar; + if (prop.type == RLMPropertyTypeObject) + ar = [[RLMArray alloc] initWithObjectClassName:prop.objectClassName]; + else + ar = [[RLMArray alloc] initWithObjectType:prop.type optional:prop.optional]; + [ar addObjects:values]; + superSet(obj, propName, ar); }; } -// implement the class method className on accessors to return the className of the -// base object -void RLMReplaceClassNameMethod(Class accessorClass, NSString *className) { - Class metaClass = object_getClass(accessorClass); - IMP imp = imp_implementationWithBlock(^(Class){ return className; }); - class_addMethod(metaClass, @selector(className), imp, "@@:"); -} - -// implement the shared schema method -void RLMReplaceSharedSchemaMethod(Class accessorClass, RLMObjectSchema *schema) { - Class metaClass = object_getClass(accessorClass); - IMP imp = imp_implementationWithBlock(^(Class cls) { - if (cls == accessorClass) { - return schema; - } - - // If we aren't being called directly on the class this was overriden - // for, the class is either a subclass which we haven't initialized yet, - // or it's a runtime-generated class which should use the parent's - // schema. We check for the latter by checking if the immediate - // descendent of the desired class is a class generated by us (there - // may be further subclasses not generated by us for things like KVO). - Class parent = class_getSuperclass(cls); - while (parent != accessorClass) { - cls = parent; - parent = class_getSuperclass(cls); - } - - static const char accessorClassPrefix[] = "RLM:"; - if (!strncmp(class_getName(cls), accessorClassPrefix, sizeof(accessorClassPrefix) - 1)) { - return schema; - } - - return [RLMSchema sharedSchemaForClass:cls]; - }); - class_addMethod(metaClass, @selector(sharedSchema), imp, "@@:"); -} - -static void addMethod(Class cls, __unsafe_unretained RLMProperty *const prop, - id (*getter)(RLMProperty *, const char *), - id (*setter)(RLMProperty *, const char *)) { +void addMethod(Class cls, __unsafe_unretained RLMProperty *const prop, + id (*getter)(RLMProperty *, const char *), + id (*setter)(RLMProperty *, const char *)) { SEL sel = prop.getterSel; auto getterMethod = class_getInstanceMethod(cls, sel); if (!getterMethod) { @@ -546,11 +448,11 @@ static void addMethod(Class cls, __unsafe_unretained RLMProperty *const prop, } } -static Class RLMCreateAccessorClass(Class objectClass, - RLMObjectSchema *schema, - const char *accessorClassName, - id (*getterGetter)(RLMProperty *, const char *), - id (*setterGetter)(RLMProperty *, const char *)) { +Class createAccessorClass(Class objectClass, + RLMObjectSchema *schema, + const char *accessorClassName, + id (*getterGetter)(RLMProperty *, const char *), + id (*setterGetter)(RLMProperty *, const char *)) { REALM_ASSERT_DEBUG(RLMIsObjectOrSubclass(objectClass)); // create and register proxy class which derives from object class @@ -573,105 +475,101 @@ static Class RLMCreateAccessorClass(Class objectClass, return accClass; } +} // anonymous namespace + +#pragma mark - Public Interface Class RLMManagedAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema, const char *name) { - return RLMCreateAccessorClass(objectClass, schema, name, RLMAccessorGetter, RLMAccessorSetter); + return createAccessorClass(objectClass, schema, name, managedGetter, managedSetter); } Class RLMUnmanagedAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema) { - return RLMCreateAccessorClass(objectClass, schema, [@"RLM:Unmanaged " stringByAppendingString:schema.className].UTF8String, - RLMAccessorUnmanagedGetter, RLMAccessorUnmanagedSetter); + return createAccessorClass(objectClass, schema, + [@"RLM:Unmanaged " stringByAppendingString:schema.className].UTF8String, + unmanagedGetter, unmanagedSetter); +} + +// implement the class method className on accessors to return the className of the +// base object +void RLMReplaceClassNameMethod(Class accessorClass, NSString *className) { + Class metaClass = object_getClass(accessorClass); + IMP imp = imp_implementationWithBlock(^(Class){ return className; }); + class_addMethod(metaClass, @selector(className), imp, "@@:"); +} + +// implement the shared schema method +void RLMReplaceSharedSchemaMethod(Class accessorClass, RLMObjectSchema *schema) { + Class metaClass = object_getClass(accessorClass); + IMP imp = imp_implementationWithBlock(^(Class cls) { + if (cls == accessorClass) { + return schema; + } + + // If we aren't being called directly on the class this was overriden + // for, the class is either a subclass which we haven't initialized yet, + // or it's a runtime-generated class which should use the parent's + // schema. We check for the latter by checking if the immediate + // descendent of the desired class is a class generated by us (there + // may be further subclasses not generated by us for things like KVO). + Class parent = class_getSuperclass(cls); + while (parent != accessorClass) { + cls = parent; + parent = class_getSuperclass(cls); + } + + static const char accessorClassPrefix[] = "RLM:"; + if (!strncmp(class_getName(cls), accessorClassPrefix, sizeof(accessorClassPrefix) - 1)) { + return schema; + } + + return [RLMSchema sharedSchemaForClass:cls]; + }); + class_addMethod(metaClass, @selector(sharedSchema), imp, "@@:"); } void RLMDynamicValidatedSet(RLMObjectBase *obj, NSString *propName, id val) { RLMObjectSchema *schema = obj->_objectSchema; RLMProperty *prop = schema[propName]; if (!prop) { - @throw RLMException(@"Invalid property name '%@' for class '%@'.", propName, obj->_objectSchema.className); + @throw RLMException(@"Invalid property name '%@' for class '%@'.", + propName, obj->_objectSchema.className); } if (prop.isPrimary) { @throw RLMException(@"Primary key can't be changed to '%@' after an object is inserted.", val); } - if (!RLMIsObjectValidForProperty(val, prop)) { - @throw RLMException(@"Invalid property value '%@' for property '%@' of class '%@'", val, propName, obj->_objectSchema.className); - } - - RLMDynamicSet(obj, prop, RLMCoerceToNil(val), RLMCreationOptionsPromoteUnmanaged); + RLMValidateValueForProperty(val, schema, prop, true); + RLMDynamicSet(obj, prop, RLMCoerceToNil(val)); } // Precondition: the property is not a primary key -void RLMDynamicSet(__unsafe_unretained RLMObjectBase *const obj, __unsafe_unretained RLMProperty *const prop, - __unsafe_unretained id const val, RLMCreationOptions creationOptions) { +void RLMDynamicSet(__unsafe_unretained RLMObjectBase *const obj, + __unsafe_unretained RLMProperty *const prop, + __unsafe_unretained id const val) { REALM_ASSERT_DEBUG(!prop.isPrimary); - bool setDefault = creationOptions & RLMCreationOptionsSetDefault; - - auto col = obj->_info->tableColumn(prop); - RLMWrapSetter(obj, prop.name, [&] { - switch (prop.type) { - case RLMPropertyTypeInt: RLMSetValue(obj, col, (NSNumber *)val, setDefault); break; - case RLMPropertyTypeFloat: RLMSetValue(obj, col, (NSNumber *)val, setDefault); break; - case RLMPropertyTypeDouble: RLMSetValue(obj, col, (NSNumber *)val, setDefault); break; - case RLMPropertyTypeBool: RLMSetValue(obj, col, (NSNumber *)val, setDefault); break; - case RLMPropertyTypeString: RLMSetValue(obj, col, (NSString *)val, setDefault); break; - case RLMPropertyTypeDate: RLMSetValue(obj, col, (NSDate *)val, setDefault); break; - case RLMPropertyTypeData: RLMSetValue(obj, col, (NSData *)val, setDefault); break; - case RLMPropertyTypeObject: { - if (!val || val == NSNull.null) { - RLMSetValue(obj, col, (RLMObjectBase *)nil, setDefault); - } - else { - auto linkedObj = RLMGetLinkedObjectForValue(obj->_realm, prop.objectClassName, val, creationOptions); - RLMSetValue(obj, col, linkedObj, setDefault); - } - break; - } - case RLMPropertyTypeArray: - if (!val || val == NSNull.null) { - RLMSetValue(obj, col, (id)nil, setDefault); - } - else { - id rawLinks = val; - NSMutableArray *links = [NSMutableArray array]; - for (id rawLink in rawLinks) { - [links addObject:RLMGetLinkedObjectForValue(obj->_realm, prop.objectClassName, rawLink, creationOptions)]; - } - RLMSetValue(obj, col, links, setDefault); - } - break; - case RLMPropertyTypeAny: - RLMSetValue(obj, col, val, setDefault); - break; - case RLMPropertyTypeLinkingObjects: - @throw RLMException(@"Linking objects properties are read-only"); - } + realm::Object o(obj->_info->realm->_realm, *obj->_info->objectSchema, obj->_row); + RLMAccessorContext c(obj); + translateError([&] { + o.set_property_value(c, prop.columnName.UTF8String, val ?: NSNull.null, false); }); } id RLMDynamicGet(__unsafe_unretained RLMObjectBase *const obj, __unsafe_unretained RLMProperty *const prop) { - NSUInteger index = prop.index; - switch (prop.type) { - case RLMPropertyTypeInt: return getBoxed(obj, index); - case RLMPropertyTypeFloat: return getBoxed(obj, index); - case RLMPropertyTypeDouble: return getBoxed(obj, index); - case RLMPropertyTypeBool: return getBoxed(obj, index); - case RLMPropertyTypeString: return RLMGetString(obj, index); - case RLMPropertyTypeDate: return RLMGetDate(obj, index); - case RLMPropertyTypeData: return RLMGetData(obj, index); - case RLMPropertyTypeObject: return RLMGetLink(obj, index); - case RLMPropertyTypeArray: return RLMGetArray(obj, index); - case RLMPropertyTypeAny: return RLMGetAnyProperty(obj, index); - case RLMPropertyTypeLinkingObjects: return RLMGetLinkingObjects(obj, prop); - } + realm::Object o(obj->_realm->_realm, *obj->_info->objectSchema, obj->_row); + RLMAccessorContext c(obj); + c.currentProperty = prop; + return RLMCoerceToNil(o.get_property_value(c, prop.columnName.UTF8String)); } -id RLMDynamicGetByName(__unsafe_unretained RLMObjectBase *const obj, __unsafe_unretained NSString *const propName, bool asList) { +id RLMDynamicGetByName(__unsafe_unretained RLMObjectBase *const obj, + __unsafe_unretained NSString *const propName, bool asList) { RLMProperty *prop = obj->_objectSchema[propName]; if (!prop) { - @throw RLMException(@"Invalid property name '%@' for class '%@'.", propName, obj->_objectSchema.className); + @throw RLMException(@"Invalid property name '%@' for class '%@'.", + propName, obj->_objectSchema.className); } - if (asList && prop.type == RLMPropertyTypeArray && prop.swiftIvar) { + if (asList && prop.array && prop.swiftIvar) { RLMListBase *list = object_getIvar(obj, prop.swiftIvar); - if (!list._rlmArray) { + if (prop.type != RLMPropertyTypeLinkingObjects && !list._rlmArray) { list._rlmArray = RLMDynamicGet(obj, prop); } return list; @@ -679,3 +577,227 @@ id RLMDynamicGetByName(__unsafe_unretained RLMObjectBase *const obj, __unsafe_un return RLMDynamicGet(obj, prop); } + +RLMAccessorContext::RLMAccessorContext(RLMAccessorContext& parent, realm::Property const& property) +: _realm(parent._realm) +, _info(property.type == realm::PropertyType::Object ? parent._info.linkTargetType(property) : parent._info) +, _promote_existing(parent._promote_existing) +{ +} + +RLMAccessorContext::RLMAccessorContext(RLMRealm *realm, RLMClassInfo& info, bool promote) +: _realm(realm), _info(info), _promote_existing(promote) +{ +} + +RLMAccessorContext::RLMAccessorContext(__unsafe_unretained RLMObjectBase *const parent, + const realm::Property *prop) +: _realm(parent->_realm) +, _info(prop && prop->type == realm::PropertyType::Object ? parent->_info->linkTargetType(*prop) + : *parent->_info) +, _parentObject(parent) +{ +} + +id RLMAccessorContext::defaultValue(__unsafe_unretained NSString *const key) { + if (!_defaultValues) { + _defaultValues = RLMDefaultValuesForObjectSchema(_info.rlmObjectSchema); + } + return _defaultValues[key]; +} + +id RLMAccessorContext::propertyValue(__unsafe_unretained id const obj, size_t propIndex, + __unsafe_unretained RLMProperty *const prop) { + // Property value from an NSArray + if ([obj respondsToSelector:@selector(objectAtIndex:)]) { + return propIndex < [obj count] ? [obj objectAtIndex:propIndex] : nil; + } + + // Property value from an NSDictionary + if ([obj respondsToSelector:@selector(objectForKey:)]) { + return [obj objectForKey:prop.name]; + } + + // Property value from an instance of this object type + id value; + if ([obj isKindOfClass:_info.rlmObjectSchema.objectClass] && prop.swiftIvar) { + if (prop.array) { + return static_cast(object_getIvar(obj, prop.swiftIvar))._rlmArray; + } + else { // optional + value = RLMGetOptional(static_cast(object_getIvar(obj, prop.swiftIvar))); + } + } + else { + // Property value from some object that's KVC-compatible + value = RLMValidatedValueForProperty(obj, [obj respondsToSelector:prop.getterSel] ? prop.getterName : prop.name, + _info.rlmObjectSchema.className); + } + return value ?: NSNull.null; +} + +id RLMAccessorContext::box(realm::List&& l) { + REALM_ASSERT(_parentObject); + REALM_ASSERT(currentProperty); + return [[RLMManagedArray alloc] initWithList:std::move(l) realm:_realm + parentInfo:_parentObject->_info + property:currentProperty]; +} + +id RLMAccessorContext::box(realm::Object&& o) { + REALM_ASSERT(currentProperty); + return RLMCreateObjectAccessor(_realm, _info.linkTargetType(currentProperty.index), o.row()); +} + +id RLMAccessorContext::box(realm::RowExpr r) { + return RLMCreateObjectAccessor(_realm, _info, r); +} + +id RLMAccessorContext::box(realm::Results&& r) { + REALM_ASSERT(currentProperty); + return [RLMResults resultsWithObjectInfo:_realm->_info[currentProperty.objectClassName] + results:std::move(r)]; +} + +template<> +realm::Timestamp RLMAccessorContext::unbox(__unsafe_unretained id const value, bool, bool) { + id v = RLMCoerceToNil(value); + return RLMTimestampForNSDate(v); +} + +template<> +bool RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) { + return [v boolValue]; +} +template<> +double RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) { + return [v doubleValue]; +} +template<> +float RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) { + return [v floatValue]; +} +template<> +long long RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) { + return [v longLongValue]; +} +template<> +realm::BinaryData RLMAccessorContext::unbox(id v, bool, bool) { + v = RLMCoerceToNil(v); + return RLMBinaryDataForNSData(v); +} +template<> +realm::StringData RLMAccessorContext::unbox(id v, bool, bool) { + v = RLMCoerceToNil(v); + return RLMStringDataWithNSString(v); +} + +template +static auto to_optional(__unsafe_unretained id const value, Fn&& fn) { + id v = RLMCoerceToNil(value); + return v && v != NSNull.null ? realm::util::make_optional(fn(v)) : realm::util::none; +} + +template<> +realm::util::Optional RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) { + return to_optional(v, [&](__unsafe_unretained id v) { return (bool)[v boolValue]; }); +} +template<> +realm::util::Optional RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) { + return to_optional(v, [&](__unsafe_unretained id v) { return [v doubleValue]; }); +} +template<> +realm::util::Optional RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) { + return to_optional(v, [&](__unsafe_unretained id v) { return [v floatValue]; }); +} +template<> +realm::util::Optional RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) { + return to_optional(v, [&](__unsafe_unretained id v) { return [v longLongValue]; }); +} + +template<> +realm::RowExpr RLMAccessorContext::unbox(__unsafe_unretained id const v, bool create, bool update) { + RLMObjectBase *link = RLMDynamicCast(v); + if (!link) { + if (!create) + return realm::RowExpr(); + return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, v, update)->_row; + } + + if (link.isInvalidated) { + if (create) { + @throw RLMException(@"Adding a deleted or invalidated object to a Realm is not permitted"); + } + else { + @throw RLMException(@"Object has been invalidated"); + } + } + + if (![link->_objectSchema.className isEqualToString:_info.rlmObjectSchema.className]) { + if (create && !_promote_existing) + return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, link, update)->_row; + return link->_row; + } + + if (!link->_realm) { + if (!create) + return realm::RowExpr(); + if (!_promote_existing) + return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, link, update)->_row; + RLMAddObjectToRealm(link, _realm, update); + } + else if (link->_realm != _realm) { + if (_promote_existing) + @throw RLMException(@"Object is already managed by another Realm. Use create instead to copy it into this Realm."); + return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, v, update)->_row; + } + return link->_row; +} + +void RLMAccessorContext::will_change(realm::Row const& row, realm::Property const& prop) { + _observationInfo = RLMGetObservationInfo(nullptr, row.get_index(), _info); + if (_observationInfo) { + _kvoPropertyName = _info.propertyForTableColumn(prop.table_column).name; + _observationInfo->willChange(_kvoPropertyName); + } +} + +void RLMAccessorContext::did_change() { + if (_observationInfo) { + _observationInfo->didChange(_kvoPropertyName); + _kvoPropertyName = nil; + _observationInfo = nullptr; + } +} + +RLMOptionalId RLMAccessorContext::value_for_property(__unsafe_unretained id const obj, + std::string const&, size_t propIndex) { + auto prop = _info.rlmObjectSchema.properties[propIndex]; + id value = propertyValue(obj, propIndex, prop); + if (value) { + RLMValidateValueForProperty(value, _info.rlmObjectSchema, prop); + } + + if (_promote_existing && [obj isKindOfClass:_info.rlmObjectSchema.objectClass] && !prop.swiftIvar) { + // set the ivars for object and array properties to nil as otherwise the + // accessors retain objects that are no longer accessible via the properties + // this is mainly an issue when the object graph being added has cycles, + // as it's not obvious that the user has to set the *ivars* to nil to + // avoid leaking memory + if (prop.type == RLMPropertyTypeObject) { + ((void(*)(id, SEL, id))objc_msgSend)(obj, prop.setterSel, nil); + } + } + + return RLMOptionalId{value}; +} + +RLMOptionalId RLMAccessorContext::default_value_for_property(realm::ObjectSchema const&, + std::string const& prop) +{ + return RLMOptionalId{defaultValue(@(prop.c_str()))}; +} + +bool RLMAccessorContext::is_same_list(realm::List const& list, __unsafe_unretained id const v) const noexcept { + return [v respondsToSelector:@selector(isBackedByList:)] && [v isBackedByList:list]; +} diff --git a/Pods/Realm/Realm/RLMAnalytics.mm b/Pods/Realm/Realm/RLMAnalytics.mm index b33afd8..b6d750a 100644 --- a/Pods/Realm/Realm/RLMAnalytics.mm +++ b/Pods/Realm/Realm/RLMAnalytics.mm @@ -48,7 +48,7 @@ // - What version of OS X it's running on (in case Xcode aggressively drops // support for older versions again, we need to know what we need to support). // - The minimum iOS/OS X version that the application is targeting (again, to -// help us decide what versions we need to support). +// help us decide what versions we need to support). // - An anonymous MAC address and bundle ID to aggregate the other information on. // - What version of Swift is being used (if applicable). diff --git a/Pods/Realm/Realm/RLMArray.mm b/Pods/Realm/Realm/RLMArray.mm index 5f08d34..2262e78 100644 --- a/Pods/Realm/Realm/RLMArray.mm +++ b/Pods/Realm/Realm/RLMArray.mm @@ -18,16 +18,16 @@ #import "RLMArray_Private.hpp" -#import "RLMObject_Private.h" -#import "RLMObjectStore.h" #import "RLMObjectSchema.h" +#import "RLMObjectStore.h" +#import "RLMObject_Private.h" +#import "RLMProperty_Private.h" #import "RLMQueryUtil.hpp" +#import "RLMSchema_Private.h" #import "RLMSwiftSupport.h" #import "RLMThreadSafeReference_Private.hpp" #import "RLMUtil.hpp" -#import - // See -countByEnumeratingWithState:objects:count @interface RLMArrayHolder : NSObject { @public @@ -46,66 +46,28 @@ @implementation RLMArray { NSMutableArray *_backingArray; } -template -static void changeArray(__unsafe_unretained RLMArray *const ar, - NSKeyValueChange kind, dispatch_block_t f, IndexSetFactory&& is) { - if (!ar->_backingArray) { - ar->_backingArray = [NSMutableArray new]; - } - - if (RLMObjectBase *parent = ar->_parentObject) { - NSIndexSet *indexes = is(); - [parent willChange:kind valuesAtIndexes:indexes forKey:ar->_key]; - f(); - [parent didChange:kind valuesAtIndexes:indexes forKey:ar->_key]; - } - else { - f(); - } -} - -static void changeArray(__unsafe_unretained RLMArray *const ar, NSKeyValueChange kind, NSUInteger index, dispatch_block_t f) { - changeArray(ar, kind, f, [=] { return [NSIndexSet indexSetWithIndex:index]; }); -} - -static void changeArray(__unsafe_unretained RLMArray *const ar, NSKeyValueChange kind, NSRange range, dispatch_block_t f) { - changeArray(ar, kind, f, [=] { return [NSIndexSet indexSetWithIndexesInRange:range]; }); -} - -static void changeArray(__unsafe_unretained RLMArray *const ar, NSKeyValueChange kind, NSIndexSet *is, dispatch_block_t f) { - changeArray(ar, kind, f, [=] { return is; }); -} +#pragma mark - Initializers - (instancetype)initWithObjectClassName:(__unsafe_unretained NSString *const)objectClassName { + REALM_ASSERT([objectClassName length] > 0); self = [super init]; if (self) { _objectClassName = objectClassName; + _type = RLMPropertyTypeObject; } return self; } -- (RLMRealm *)realm { - return nil; -} - -// -// Generic implementations for all RLMArray variants -// - -- (id)firstObject { - if (self.count) { - return [self objectAtIndex:0]; +- (instancetype)initWithObjectType:(RLMPropertyType)type optional:(BOOL)optional { + self = [super init]; + if (self) { + _type = type; + _optional = optional; } - return nil; + return self; } -- (id)lastObject { - NSUInteger count = self.count; - if (count) { - return [self objectAtIndex:count-1]; - } - return nil; -} +#pragma mark - Convenience wrappers used for all RLMArray types - (void)addObjects:(id)objects { for (id obj in objects) { @@ -113,7 +75,7 @@ - (void)addObjects:(id)objects { } } -- (void)addObject:(RLMObject *)object { +- (void)addObject:(id)object { [self insertObject:object atIndex:self.count]; } @@ -132,38 +94,46 @@ - (void)setObject:(id)newValue atIndexedSubscript:(NSUInteger)index { [self replaceObjectAtIndex:index withObject:newValue]; } -// -// Unmanaged RLMArray implementation -// +- (RLMResults *)sortedResultsUsingKeyPath:(NSString *)keyPath ascending:(BOOL)ascending { + return [self sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:keyPath ascending:ascending]]]; +} -static void RLMValidateMatchingObjectType(RLMArray *array, RLMObject *object) { - if (!object) { - @throw RLMException(@"Object must not be nil"); - } - if (!object->_objectSchema) { - @throw RLMException(@"Object cannot be inserted unless the schema is initialized. " - "This can happen if you try to insert objects into a RLMArray / List from a default value or from an overriden unmanaged initializer (`init()`)."); - } - if (![array->_objectClassName isEqualToString:object->_objectSchema.className]) { - @throw RLMException(@"Object type '%@' does not match RLMArray type '%@'.", - object->_objectSchema.className, array->_objectClassName); +- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat, ... { + va_list args; + va_start(args, predicateFormat); + NSUInteger index = [self indexOfObjectWhere:predicateFormat args:args]; + va_end(args); + return index; +} + +- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat args:(va_list)args { + return [self indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:predicateFormat + arguments:args]]; +} + +#pragma mark - Unmanaged RLMArray implementation + +- (RLMRealm *)realm { + return nil; +} + +- (id)firstObject { + if (self.count) { + return [self objectAtIndex:0]; } + return nil; } -static void RLMValidateArrayBounds(__unsafe_unretained RLMArray *const ar, - NSUInteger index, bool allowOnePastEnd=false) { - NSUInteger max = ar->_backingArray.count + allowOnePastEnd; - if (index >= max) { - @throw RLMException(@"Index %llu is out of bounds (must be less than %llu).", - (unsigned long long)index, (unsigned long long)max); +- (id)lastObject { + NSUInteger count = self.count; + if (count) { + return [self objectAtIndex:count-1]; } + return nil; } - (id)objectAtIndex:(NSUInteger)index { - RLMValidateArrayBounds(self, index); - if (!_backingArray) { - _backingArray = [NSMutableArray new]; - } + validateArrayBounds(self, index); return [_backingArray objectAtIndex:index]; } @@ -175,7 +145,9 @@ - (BOOL)isInvalidated { return NO; } -- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(__unused __unsafe_unretained id [])buffer count:(__unused NSUInteger)len { +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state + objects:(__unused __unsafe_unretained id [])buffer + count:(__unused NSUInteger)len { if (state->state != 0) { return 0; } @@ -204,18 +176,90 @@ - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state object return i; } + +template +static void changeArray(__unsafe_unretained RLMArray *const ar, + NSKeyValueChange kind, dispatch_block_t f, IndexSetFactory&& is) { + if (!ar->_backingArray) { + ar->_backingArray = [NSMutableArray new]; + } + + if (RLMObjectBase *parent = ar->_parentObject) { + NSIndexSet *indexes = is(); + [parent willChange:kind valuesAtIndexes:indexes forKey:ar->_key]; + f(); + [parent didChange:kind valuesAtIndexes:indexes forKey:ar->_key]; + } + else { + f(); + } +} + +static void changeArray(__unsafe_unretained RLMArray *const ar, NSKeyValueChange kind, + NSUInteger index, dispatch_block_t f) { + changeArray(ar, kind, f, [=] { return [NSIndexSet indexSetWithIndex:index]; }); +} + +static void changeArray(__unsafe_unretained RLMArray *const ar, NSKeyValueChange kind, + NSRange range, dispatch_block_t f) { + changeArray(ar, kind, f, [=] { return [NSIndexSet indexSetWithIndexesInRange:range]; }); +} + +static void changeArray(__unsafe_unretained RLMArray *const ar, NSKeyValueChange kind, + NSIndexSet *is, dispatch_block_t f) { + changeArray(ar, kind, f, [=] { return is; }); +} + +void RLMArrayValidateMatchingObjectType(__unsafe_unretained RLMArray *const array, + __unsafe_unretained id const value) { + if (!value && !array->_optional) { + @throw RLMException(@"Invalid nil value for array of '%@'.", + array->_objectClassName ?: RLMTypeToString(array->_type)); + } + if (array->_type != RLMPropertyTypeObject) { + if (!RLMValidateValue(value, array->_type, array->_optional, false, nil)) { + @throw RLMException(@"Invalid value '%@' of type '%@' for expected type '%@%s'.", + value, [value class], RLMTypeToString(array->_type), + array->_optional ? "?" : ""); + } + return; + } + + auto object = RLMDynamicCast(value); + if (!object) { + return; + } + if (!object->_objectSchema) { + @throw RLMException(@"Object cannot be inserted unless the schema is initialized. " + "This can happen if you try to insert objects into a RLMArray / List from a default value or from an overriden unmanaged initializer (`init()`)."); + } + if (![array->_objectClassName isEqualToString:object->_objectSchema.className]) { + @throw RLMException(@"Object of type '%@' does not match RLMArray type '%@'.", + object->_objectSchema.className, array->_objectClassName); + } +} + +static void validateArrayBounds(__unsafe_unretained RLMArray *const ar, + NSUInteger index, bool allowOnePastEnd=false) { + NSUInteger max = ar->_backingArray.count + allowOnePastEnd; + if (index >= max) { + @throw RLMException(@"Index %llu is out of bounds (must be less than %llu).", + (unsigned long long)index, (unsigned long long)max); + } +} + - (void)addObjectsFromArray:(NSArray *)array { for (id obj in array) { - RLMValidateMatchingObjectType(self, obj); + RLMArrayValidateMatchingObjectType(self, obj); } changeArray(self, NSKeyValueChangeInsertion, NSMakeRange(_backingArray.count, array.count), ^{ [_backingArray addObjectsFromArray:array]; }); } -- (void)insertObject:(RLMObject *)anObject atIndex:(NSUInteger)index { - RLMValidateMatchingObjectType(self, anObject); - RLMValidateArrayBounds(self, index, true); +- (void)insertObject:(id)anObject atIndex:(NSUInteger)index { + RLMArrayValidateMatchingObjectType(self, anObject); + validateArrayBounds(self, index, true); changeArray(self, NSKeyValueChangeInsertion, index, ^{ [_backingArray insertObject:anObject atIndex:index]; }); @@ -225,7 +269,7 @@ - (void)insertObjects:(id)objects atIndexes:(NSIndexSet *)ind changeArray(self, NSKeyValueChangeInsertion, indexes, ^{ NSUInteger currentIndex = [indexes firstIndex]; for (RLMObject *obj in objects) { - RLMValidateMatchingObjectType(self, obj); + RLMArrayValidateMatchingObjectType(self, obj); [_backingArray insertObject:obj atIndex:currentIndex]; currentIndex = [indexes indexGreaterThanIndex:currentIndex]; } @@ -233,7 +277,7 @@ - (void)insertObjects:(id)objects atIndexes:(NSIndexSet *)ind } - (void)removeObjectAtIndex:(NSUInteger)index { - RLMValidateArrayBounds(self, index); + validateArrayBounds(self, index); changeArray(self, NSKeyValueChangeRemoval, index, ^{ [_backingArray removeObjectAtIndex:index]; }); @@ -246,17 +290,17 @@ - (void)removeObjectsAtIndexes:(NSIndexSet *)indexes { } - (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject { - RLMValidateMatchingObjectType(self, anObject); - RLMValidateArrayBounds(self, index); + RLMArrayValidateMatchingObjectType(self, anObject); + validateArrayBounds(self, index); changeArray(self, NSKeyValueChangeReplacement, index, ^{ [_backingArray replaceObjectAtIndex:index withObject:anObject]; }); } - (void)moveObjectAtIndex:(NSUInteger)sourceIndex toIndex:(NSUInteger)destinationIndex { - RLMValidateArrayBounds(self, sourceIndex); - RLMValidateArrayBounds(self, destinationIndex); - RLMObjectBase *original = _backingArray[sourceIndex]; + validateArrayBounds(self, sourceIndex); + validateArrayBounds(self, destinationIndex); + id original = _backingArray[sourceIndex]; auto start = std::min(sourceIndex, destinationIndex); auto len = std::max(sourceIndex, destinationIndex) - start + 1; @@ -267,8 +311,8 @@ - (void)moveObjectAtIndex:(NSUInteger)sourceIndex toIndex:(NSUInteger)destinatio } - (void)exchangeObjectAtIndex:(NSUInteger)index1 withObjectAtIndex:(NSUInteger)index2 { - RLMValidateArrayBounds(self, index1); - RLMValidateArrayBounds(self, index2); + validateArrayBounds(self, index1); + validateArrayBounds(self, index2); changeArray(self, NSKeyValueChangeReplacement, ^{ [_backingArray exchangeObjectAtIndex:index1 withObjectAtIndex:index2]; @@ -279,10 +323,17 @@ - (void)exchangeObjectAtIndex:(NSUInteger)index1 withObjectAtIndex:(NSUInteger)i }); } -- (NSUInteger)indexOfObject:(RLMObject *)object { - RLMValidateMatchingObjectType(self, object); +- (NSUInteger)indexOfObject:(id)object { + RLMArrayValidateMatchingObjectType(self, object); + if (!_backingArray) { + return NSNotFound; + } + if (_type != RLMPropertyTypeObject) { + return [_backingArray indexOfObject:object]; + } + NSUInteger index = 0; - for (RLMObject *cmp in _backingArray) { + for (RLMObjectBase *cmp in _backingArray) { if (RLMObjectBaseAreEqual(object, cmp)) { return index; } @@ -297,8 +348,7 @@ - (void)removeAllObjects { }); } -- (RLMResults *)objectsWhere:(NSString *)predicateFormat, ... -{ +- (RLMResults *)objectsWhere:(NSString *)predicateFormat, ... { va_list args; va_start(args, predicateFormat); RLMResults *results = [self objectsWhere:predicateFormat args:args]; @@ -306,28 +356,104 @@ - (RLMResults *)objectsWhere:(NSString *)predicateFormat, ... return results; } -- (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args -{ +- (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args { return [self objectsWithPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]]; } -- (id)valueForKeyPath:(NSString *)keyPath { - if (!_backingArray) { - return [super valueForKeyPath:keyPath]; +static bool canAggregate(RLMPropertyType type, bool allowDate) { + switch (type) { + case RLMPropertyTypeInt: + case RLMPropertyTypeFloat: + case RLMPropertyTypeDouble: + return true; + case RLMPropertyTypeDate: + return allowDate; + default: + return false; + } +} + +- (RLMPropertyType)typeForProperty:(NSString *)propertyName { + if ([propertyName isEqualToString:@"self"]) { + return _type; + } + + RLMObjectSchema *objectSchema; + if (_backingArray.count) { + objectSchema = [_backingArray[0] objectSchema]; } + else { + objectSchema = [RLMSchema.partialPrivateSharedSchema schemaForClassName:_objectClassName]; + } + + return RLMValidatedProperty(objectSchema, propertyName).type; +} + +- (id)aggregateProperty:(NSString *)key operation:(NSString *)op method:(SEL)sel { // Although delegating to valueForKeyPath: here would allow to support // nested key paths as well, limiting functionality gives consistency // between unmanaged and managed arrays. - if ([keyPath characterAtIndex:0] == '@') { - NSRange operatorRange = [keyPath rangeOfString:@"." options:NSLiteralSearch]; - if (operatorRange.location != NSNotFound) { - NSString *operatorKeyPath = [keyPath substringFromIndex:operatorRange.location + 1]; - if ([operatorKeyPath rangeOfString:@"."].location != NSNotFound) { - @throw RLMException(@"Nested key paths are not supported yet for KVC collection operators."); - } + if ([key rangeOfString:@"."].location != NSNotFound) { + @throw RLMException(@"Nested key paths are not supported yet for KVC collection operators."); + } + + bool allowDate = false; + bool sum = false; + if ([op isEqualToString:@"@min"] || [op isEqualToString:@"@max"]) { + allowDate = true; + } + else if ([op isEqualToString:@"@sum"]) { + sum = true; + } + else if (![op isEqualToString:@"@avg"]) { + // Just delegate to NSArray for all other operators + return [_backingArray valueForKeyPath:[op stringByAppendingPathExtension:key]]; + } + + RLMPropertyType type = [self typeForProperty:key]; + if (!canAggregate(type, allowDate)) { + NSString *method = sel ? NSStringFromSelector(sel) : op; + if (_type == RLMPropertyTypeObject) { + @throw RLMException(@"%@: is not supported for %@ property '%@.%@'", + method, RLMTypeToString(type), _objectClassName, key); + } + else { + @throw RLMException(@"%@ is not supported for %@%s array", + method, RLMTypeToString(_type), _optional ? "?" : ""); + } + } + + NSArray *values = [key isEqualToString:@"self"] ? _backingArray : [_backingArray valueForKey:key]; + if (_optional) { + // Filter out NSNull values to match our behavior on managed arrays + NSIndexSet *nonnull = [values indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger, BOOL *) { + return obj != NSNull.null; + }]; + if (nonnull.count < values.count) { + values = [values objectsAtIndexes:nonnull]; } } - return [_backingArray valueForKeyPath:keyPath]; + id result = [values valueForKeyPath:[op stringByAppendingString:@".self"]]; + return sum && !result ? @0 : result; +} + +- (id)valueForKeyPath:(NSString *)keyPath { + if ([keyPath characterAtIndex:0] != '@') { + return _backingArray ? [_backingArray valueForKeyPath:keyPath] : [super valueForKeyPath:keyPath]; + } + + if (!_backingArray) { + _backingArray = [NSMutableArray new]; + } + + NSUInteger dot = [keyPath rangeOfString:@"."].location; + if (dot == NSNotFound) { + return [_backingArray valueForKeyPath:keyPath]; + } + + NSString *op = [keyPath substringToIndex:dot]; + NSString *key = [keyPath substringFromIndex:dot + 1]; + return [self aggregateProperty:key operation:op method:nil]; } - (id)valueForKey:(NSString *)key { @@ -335,13 +461,41 @@ - (id)valueForKey:(NSString *)key { return @NO; // Unmanaged arrays are never invalidated } if (!_backingArray) { - return @[]; + _backingArray = [NSMutableArray new]; } return [_backingArray valueForKey:key]; } - (void)setValue:(id)value forKey:(NSString *)key { - [_backingArray setValue:value forKey:key]; + if ([key isEqualToString:@"self"]) { + RLMArrayValidateMatchingObjectType(self, value); + for (NSUInteger i = 0, count = _backingArray.count; i < count; ++i) { + _backingArray[i] = value; + } + return; + } + else if (_type == RLMPropertyTypeObject) { + [_backingArray setValue:value forKey:key]; + } + else { + [self setValue:value forUndefinedKey:key]; + } +} + +- (id)minOfProperty:(NSString *)property { + return [self aggregateProperty:property operation:@"@min" method:_cmd]; +} + +- (id)maxOfProperty:(NSString *)property { + return [self aggregateProperty:property operation:@"@max" method:_cmd]; +} + +- (id)sumOfProperty:(NSString *)property { + return [self aggregateProperty:property operation:@"@sum" method:_cmd]; +} + +- (id)averageOfProperty:(NSString *)property { + return [self aggregateProperty:property operation:@"@avg" method:_cmd]; } - (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate { @@ -360,35 +514,22 @@ - (NSArray *)objectsAtIndexes:(NSIndexSet *)indexes { return [_backingArray objectsAtIndexes:indexes]; } -- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context { +- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath + options:(NSKeyValueObservingOptions)options context:(void *)context { RLMValidateArrayObservationKey(keyPath, self); [super addObserver:observer forKeyPath:keyPath options:options context:context]; } -// -// Methods unsupported on unmanaged RLMArray instances -// +#pragma mark - Methods unsupported on unmanaged RLMArray instances #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-parameter" -- (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate -{ +- (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate { @throw RLMException(@"This method may only be called on RLMArray instances retrieved from an RLMRealm"); } -- (RLMResults *)sortedResultsUsingKeyPath:(NSString *)keyPath ascending:(BOOL)ascending -{ - return [self sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:keyPath ascending:ascending]]]; -} - -- (RLMResults *)sortedResultsUsingProperty:(NSString *)property ascending:(BOOL)ascending -{ - return [self sortedResultsUsingKeyPath:property ascending:ascending]; -} - -- (RLMResults *)sortedResultsUsingDescriptors:(NSArray *)properties -{ +- (RLMResults *)sortedResultsUsingDescriptors:(NSArray *)properties { @throw RLMException(@"This method may only be called on RLMArray instances retrieved from an RLMRealm"); } @@ -400,32 +541,6 @@ - (RLMResults *)sortedResultsUsingDescriptors:(NSArray *)pr - (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMArray *, RLMCollectionChange *, NSError *))block { @throw RLMException(@"This method may only be called on RLMArray instances retrieved from an RLMRealm"); } -#pragma clang diagnostic pop - -- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat, ... -{ - va_list args; - va_start(args, predicateFormat); - NSUInteger index = [self indexOfObjectWhere:predicateFormat args:args]; - va_end(args); - return index; -} - -- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat args:(va_list)args -{ - return [self indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:predicateFormat - arguments:args]]; -} - -#pragma mark - Superclass Overrides - -- (NSString *)description { - return [self descriptionWithMaxDepth:RLMDescriptionMaxDepth]; -} - -- (NSString *)descriptionWithMaxDepth:(NSUInteger)depth { - return RLMDescriptionWithMaxDepth(@"RLMArray", self, depth); -} #pragma mark - Thread Confined Protocol Conformance @@ -437,12 +552,23 @@ - (id)objectiveCMetadata { REALM_TERMINATE("Unexpected handover of unmanaged `RLMArray`"); } -+ (instancetype)objectWithThreadSafeReference:(__unused std::unique_ptr)reference - metadata:(__unused id)metadata - realm:(__unused RLMRealm *)realm { ++ (instancetype)objectWithThreadSafeReference:(std::unique_ptr)reference + metadata:(id)metadata + realm:(RLMRealm *)realm { REALM_TERMINATE("Unexpected handover of unmanaged `RLMArray`"); } +#pragma clang diagnostic pop // unused parameter warning + +#pragma mark - Superclass Overrides + +- (NSString *)description { + return [self descriptionWithMaxDepth:RLMDescriptionMaxDepth]; +} + +- (NSString *)descriptionWithMaxDepth:(NSUInteger)depth { + return RLMDescriptionWithMaxDepth(@"RLMArray", self, depth); +} @end @implementation RLMSortDescriptor @@ -454,16 +580,8 @@ + (instancetype)sortDescriptorWithKeyPath:(NSString *)keyPath ascending:(BOOL)as return desc; } -+ (instancetype)sortDescriptorWithProperty:(NSString *)propertyName ascending:(BOOL)ascending { - return [RLMSortDescriptor sortDescriptorWithKeyPath:propertyName ascending:ascending]; -} - - (instancetype)reversedSortDescriptor { return [self.class sortDescriptorWithKeyPath:_keyPath ascending:!_ascending]; } -- (NSString *)property { - return _keyPath; -} - @end diff --git a/Pods/Realm/Realm/RLMAuthResponseModel.m b/Pods/Realm/Realm/RLMAuthResponseModel.m deleted file mode 100644 index 2eb5b7c..0000000 --- a/Pods/Realm/Realm/RLMAuthResponseModel.m +++ /dev/null @@ -1,57 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#import "RLMAuthResponseModel.h" - -#import "RLMTokenModels.h" -#import "RLMSyncUtil_Private.h" - -static const NSString *const kRLMSyncAccessTokenKey = @"access_token"; -static const NSString *const kRLMSyncRefreshTokenKey = @"refresh_token"; - -@interface RLMAuthResponseModel () - -@property (nonatomic, readwrite) RLMTokenModel *accessToken; -@property (nonatomic, readwrite) RLMTokenModel *refreshToken; - -@end - -@implementation RLMAuthResponseModel - -- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary - requireAccessToken:(BOOL)requireAccessToken - requireRefreshToken:(BOOL)requireRefreshToken { - if (self = [super init]) { - // Get the access token. - if (requireAccessToken) { - RLM_SYNC_PARSE_MODEL_OR_ABORT(jsonDictionary, kRLMSyncAccessTokenKey, RLMTokenModel, accessToken); - } else { - RLM_SYNC_PARSE_OPTIONAL_MODEL(jsonDictionary, kRLMSyncAccessTokenKey, RLMTokenModel, accessToken); - } - // Get the refresh token. - if (requireRefreshToken) { - RLM_SYNC_PARSE_MODEL_OR_ABORT(jsonDictionary, kRLMSyncRefreshTokenKey, RLMTokenModel, refreshToken); - } else { - RLM_SYNC_PARSE_OPTIONAL_MODEL(jsonDictionary, kRLMSyncRefreshTokenKey, RLMTokenModel, refreshToken); - } - return self; - } - return nil; -} - -@end diff --git a/Pods/Realm/Realm/RLMClassInfo.mm b/Pods/Realm/Realm/RLMClassInfo.mm index a97bf05..52b44f8 100644 --- a/Pods/Realm/Realm/RLMClassInfo.mm +++ b/Pods/Realm/Realm/RLMClassInfo.mm @@ -28,6 +28,7 @@ #import "object_schema.hpp" #import "object_store.hpp" #import "schema.hpp" +#import "shared_realm.hpp" #import @@ -77,6 +78,11 @@ return *m_linkTargets[propertyIndex]; } +RLMClassInfo &RLMClassInfo::linkTargetType(realm::Property const& property) { + REALM_ASSERT(property.type == PropertyType::Object); + return linkTargetType(&property - &objectSchema->persisted_properties[0]); +} + RLMSchemaInfo::impl::iterator RLMSchemaInfo::begin() noexcept { return m_objects.begin(); } RLMSchemaInfo::impl::iterator RLMSchemaInfo::end() noexcept { return m_objects.end(); } RLMSchemaInfo::impl::const_iterator RLMSchemaInfo::begin() const noexcept { return m_objects.begin(); } @@ -93,9 +99,11 @@ return *&it->second; } -RLMSchemaInfo::RLMSchemaInfo(RLMRealm *realm, RLMSchema *rlmSchema, realm::Schema const& schema) { - REALM_ASSERT(rlmSchema.objectSchema.count == schema.size()); - REALM_ASSERT(m_objects.empty()); +RLMSchemaInfo::RLMSchemaInfo(RLMRealm *realm) { + RLMSchema *rlmSchema = realm.schema; + realm::Schema const& schema = realm->_realm->schema(); + // rlmSchema can be larger due to multiple classes backed by one table + REALM_ASSERT(rlmSchema.objectSchema.count >= schema.size()); m_objects.reserve(schema.size()); for (RLMObjectSchema *rlmObjectSchema in rlmSchema.objectSchema) { @@ -105,3 +113,19 @@ &*schema.find(rlmObjectSchema.objectName.UTF8String))); } } + +RLMSchemaInfo RLMSchemaInfo::clone(realm::Schema const& source_schema, + __unsafe_unretained RLMRealm *const target_realm) { + RLMSchemaInfo info; + info.m_objects.reserve(m_objects.size()); + + auto& schema = target_realm->_realm->schema(); + for (auto& pair : m_objects) { + size_t idx = pair.second.objectSchema - &*source_schema.begin(); + info.m_objects.emplace(std::piecewise_construct, + std::forward_as_tuple(pair.first), + std::forward_as_tuple(target_realm, pair.second.rlmObjectSchema, + &*schema.begin() + idx)); + } + return info; +} diff --git a/Pods/Realm/Realm/RLMCollection.mm b/Pods/Realm/Realm/RLMCollection.mm index b039d99..360789a 100644 --- a/Pods/Realm/Realm/RLMCollection.mm +++ b/Pods/Realm/Realm/RLMCollection.mm @@ -18,17 +18,18 @@ #import "RLMCollection_Private.hpp" -#import "RLMArray_Private.h" +#import "RLMAccessor.hpp" +#import "RLMArray_Private.hpp" +#import "RLMListBase.h" #import "RLMObjectSchema_Private.hpp" #import "RLMObjectStore.h" #import "RLMObject_Private.hpp" +#import "RLMProperty_Private.h" #import "collection_notifications.hpp" #import "list.hpp" #import "results.hpp" -#import - static const int RLMEnumerationBufferSize = 16; @implementation RLMFastEnumerator { @@ -41,27 +42,56 @@ @implementation RLMFastEnumerator { RLMRealm *_realm; RLMClassInfo *_info; - // Collection being enumerated. Only one of these two will be valid: when - // possible we enumerate the collection directly, but when in a write - // transaction we instead create a frozen TableView and enumerate that - // instead so that mutating the collection during enumeration works. - id _collection; - realm::TableView _tableView; + // A pointer to either _snapshot or a Results from the source collection, + // to avoid having to copy the Results when not in a write transaction + realm::Results *_results; + realm::Results _snapshot; + + // A strong reference to the collection being enumerated to ensure it stays + // alive when we're holding a pointer to a member in it + id _collection; } -- (instancetype)initWithCollection:(id)collection objectSchema:(RLMClassInfo&)info { +- (instancetype)initWithList:(realm::List&)list + collection:(id)collection + realm:(RLMRealm *)realm + classInfo:(RLMClassInfo&)info +{ self = [super init]; if (self) { - _realm = collection.realm; + if (realm.inWriteTransaction) { + _snapshot = list.snapshot(); + } + else { + _snapshot = list.as_results(); + _collection = collection; + [realm registerEnumerator:self]; + } + _results = &_snapshot; + _realm = realm; _info = &info; + } + return self; +} - if (_realm.inWriteTransaction) { - _tableView = [collection tableView]; +- (instancetype)initWithResults:(realm::Results&)results + collection:(id)collection + realm:(RLMRealm *)realm + classInfo:(RLMClassInfo&)info +{ + self = [super init]; + if (self) { + if (realm.inWriteTransaction) { + _snapshot = results.snapshot(); + _results = &_snapshot; } else { + _results = &results; _collection = collection; - [_realm registerEnumerator:self]; + [realm registerEnumerator:self]; } + _realm = realm; + _info = &info; } return self; } @@ -73,14 +103,15 @@ - (void)dealloc { } - (void)detach { - _tableView = [_collection tableView]; + _snapshot = _results->snapshot(); + _results = &_snapshot; _collection = nil; } - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state count:(NSUInteger)len { [_realm verifyThread]; - if (!_tableView.is_attached() && !_collection) { + if (!_results->is_valid()) { @throw RLMException(@"Collection is no longer valid"); } // The fast enumeration buffer size is currently a hardcoded number in the @@ -92,18 +123,12 @@ - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state NSUInteger batchCount = 0, count = state->extra[1]; - Class accessorClass = _info->rlmObjectSchema.accessorClass; - for (NSUInteger index = state->state; index < count && batchCount < len; ++index) { - RLMObject *accessor = RLMCreateManagedAccessor(accessorClass, _realm, _info); - if (_collection) { - accessor->_row = (*_info->table())[[_collection indexInSource:index]]; + @autoreleasepool { + RLMAccessorContext ctx(_realm, *_info); + for (NSUInteger index = state->state; index < count && batchCount < len; ++index) { + _strongBuffer[batchCount] = _results->get(ctx, index); + batchCount++; } - else if (_tableView.is_row_attached(index)) { - accessor->_row = (*_info->table())[_tableView.get_source_ndx(index)]; - } - RLMInitializeSwiftAccessorGenerics(accessor); - _strongBuffer[batchCount] = accessor; - batchCount++; } for (NSUInteger i = batchCount; i < len; ++i) { @@ -113,13 +138,11 @@ - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state if (batchCount == 0) { // Release our data if we're done, as we're autoreleased and so may // stick around for a while - _collection = nil; - if (_tableView.is_attached()) { - _tableView = {}; - } - else { + if (_collection) { + _collection = nil; [_realm unregisterEnumerator:self]; } + _snapshot = {}; } state->itemsPtr = (__unsafe_unretained id *)(void *)_strongBuffer; @@ -130,37 +153,81 @@ - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state } @end +NSUInteger RLMFastEnumerate(NSFastEnumerationState *state, NSUInteger len, id collection) { + __autoreleasing RLMFastEnumerator *enumerator; + if (state->state == 0) { + enumerator = collection.fastEnumerator; + state->extra[0] = (long)enumerator; + state->extra[1] = collection.count; + } + else { + enumerator = (__bridge id)(void *)state->extra[0]; + } + + return [enumerator countByEnumeratingWithState:state count:len]; +} -NSArray *RLMCollectionValueForKey(id collection, NSString *key) { - size_t count = collection.count; +template +NSArray *RLMCollectionValueForKey(Collection& collection, NSString *key, + RLMRealm *realm, RLMClassInfo& info) { + size_t count = collection.size(); if (count == 0) { return @[]; } - RLMRealm *realm = collection.realm; - RLMClassInfo *info = collection.objectInfo; - - NSMutableArray *results = [NSMutableArray arrayWithCapacity:count]; + NSMutableArray *array = [NSMutableArray arrayWithCapacity:count]; if ([key isEqualToString:@"self"]) { - for (size_t i = 0; i < count; i++) { - size_t rowIndex = [collection indexInSource:i]; - [results addObject:RLMCreateObjectAccessor(realm, *info, rowIndex) ?: NSNull.null]; + RLMAccessorContext context(realm, info); + for (size_t i = 0; i < count; ++i) { + [array addObject:collection.get(context, i) ?: NSNull.null]; + } + return array; + } + + if (collection.get_type() != realm::PropertyType::Object) { + RLMAccessorContext context(realm, info); + for (size_t i = 0; i < count; ++i) { + [array addObject:[collection.get(context, i) valueForKey:key] ?: NSNull.null]; + } + return array; + } + + RLMObject *accessor = RLMCreateManagedAccessor(info.rlmObjectSchema.accessorClass, realm, &info); + + // List properties need to be handled specially since we need to create a + // new List each time + if (info.rlmObjectSchema.isSwiftClass) { + auto prop = info.rlmObjectSchema[key]; + if (prop && prop.array && prop.swiftIvar) { + // Grab the actual class for the generic List from an instance of it + // so that we can make instances of the List without creating a new + // object accessor each time + Class cls = [object_getIvar(accessor, prop.swiftIvar) class]; + RLMAccessorContext context(realm, info); + for (size_t i = 0; i < count; ++i) { + RLMListBase *list = [[cls alloc] init]; + list._rlmArray = [[RLMManagedArray alloc] initWithList:realm::List(realm->_realm, *info.table(), + info.tableColumn(prop), + collection.get(i).get_index()) + realm:realm parentInfo:&info + property:prop]; + [array addObject:list]; + } + return array; } - return results; } - RLMObject *accessor = RLMCreateManagedAccessor(info->rlmObjectSchema.accessorClass, realm, info); - realm::Table *table = info->table(); for (size_t i = 0; i < count; i++) { - size_t rowIndex = [collection indexInSource:i]; - accessor->_row = (*table)[rowIndex]; + accessor->_row = collection.get(i); RLMInitializeSwiftAccessorGenerics(accessor); - [results addObject:[accessor valueForKey:key] ?: NSNull.null]; + [array addObject:[accessor valueForKey:key] ?: NSNull.null]; } - - return results; + return array; } +template NSArray *RLMCollectionValueForKey(realm::Results&, NSString *, RLMRealm *, RLMClassInfo&); +template NSArray *RLMCollectionValueForKey(realm::List&, NSString *, RLMRealm *, RLMClassInfo&); + void RLMCollectionSetValueForKey(id collection, NSString *key, id value) { realm::TableView tv = [collection tableView]; if (tv.size() == 0) { @@ -185,7 +252,9 @@ void RLMCollectionSetValueForKey(id collection, NSString *key } const NSUInteger maxObjects = 100; - auto str = [NSMutableString stringWithFormat:@"%@ <%p> (\n", name, (void *)collection]; + auto str = [NSMutableString stringWithFormat:@"%@<%@> <%p> (\n", name, + [collection objectClassName] ?: RLMTypeToString([collection type]), + (void *)collection]; size_t index = 0, skipped = 0; for (id obj in collection) { NSString *sub; @@ -217,6 +286,18 @@ void RLMCollectionSetValueForKey(id collection, NSString *key return str; } +std::vector> RLMSortDescriptorsToKeypathArray(NSArray *properties) { + std::vector> keypaths; + keypaths.reserve(properties.count); + for (RLMSortDescriptor *desc in properties) { + if ([desc.keyPath rangeOfString:@"@"].location != NSNotFound) { + @throw RLMException(@"Cannot sort on key path '%@': KVC collection operators are not supported.", desc.keyPath); + } + keypaths.push_back({desc.keyPath.UTF8String, desc.ascending}); + } + return keypaths; +} + @implementation RLMCancellationToken { realm::NotificationToken _token; __unsafe_unretained RLMRealm *_realm; @@ -238,7 +319,7 @@ - (void)suppressNextNotification { _token.suppress_next(); } -- (void)stop { +- (void)invalidate { _token = {}; } @@ -306,15 +387,6 @@ - (NSArray *)modifications { Collection& collection, void (^block)(id, RLMCollectionChange *, NSError *), bool suppressInitialChange) { - struct IsValid { - static bool call(realm::List const& list) { - return list.is_valid(); - } - static bool call(realm::Results const&) { - return true; - } - }; - auto skip = suppressInitialChange ? std::make_shared(true) : nullptr; auto cb = [=, &collection](realm::CollectionChangeSet const& changes, std::exception_ptr err) { @@ -330,10 +402,6 @@ static bool call(realm::Results const&) { } } - if (!IsValid::call(collection)) { - return; - } - if (skip && *skip) { *skip = false; block(objcCollection, nil, nil); diff --git a/Pods/Realm/Realm/RLMConstants.m b/Pods/Realm/Realm/RLMConstants.m index 7136127..b45638f 100644 --- a/Pods/Realm/Realm/RLMConstants.m +++ b/Pods/Realm/Realm/RLMConstants.m @@ -32,3 +32,5 @@ NSString * const RLMRealmCoreVersionKey = @"RLMRealmCoreVersion"; NSString * const RLMInvalidatedKey = @"invalidated"; + +NSString * const RLMBackupRealmConfigurationErrorKey = @"RLMBackupRealmConfiguration"; diff --git a/Pods/Realm/Realm/RLMJSONModels.m b/Pods/Realm/Realm/RLMJSONModels.m new file mode 100644 index 0000000..3dc03ea --- /dev/null +++ b/Pods/Realm/Realm/RLMJSONModels.m @@ -0,0 +1,232 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2017 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMJSONModels.h" +#import "RLMSyncUtil_Private.h" +#import "RLMSyncUser.h" + +#pragma mark - Constants + +static const NSString *const kRLMSyncAccessTokenKey = @"access_token"; +static const NSString *const kRLMSyncAccountsKey = @"accounts"; +static const NSString *const kRLMSyncErrorCodeKey = @"code"; +static const NSString *const kRLMSyncExpiresKey = @"expires"; +static const NSString *const kRLMSyncErrorHintKey = @"hint"; +static const NSString *const kRLMSyncIdKey = @"id"; +static const NSString *const kRLMSyncKeyKey = @"key"; +static const NSString *const kRLMSyncMetadataKey = @"metadata"; +static const NSString *const kRLMSyncRefreshTokenKey = @"refresh_token"; +static const NSString *const kRLMSyncErrorStatusKey = @"status"; +static const NSString *const kRLMSyncErrorTitleKey = @"title"; +static const NSString *const kRLMSyncTokenDataKey = @"token_data"; +static const NSString *const kRLMSyncUserKey = @"user"; +static const NSString *const kRLMSyncValueKey = @"value"; + +#pragma mark - RLMTokenDataModel + +@interface RLMTokenDataModel () + +@property (nonatomic, readwrite) NSString *identity; +@property (nonatomic, readwrite) NSString *appID; +@property (nonatomic, readwrite) NSString *path; +@property (nonatomic, readwrite) NSTimeInterval expires; +@property (nonatomic, readwrite) BOOL isAdmin; +//@property (nonatomic, readwrite) NSArray *access; + +@end + +@implementation RLMTokenDataModel + +- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary { + if (self = [super init]) { + self.isAdmin = NO; + RLM_SYNC_PARSE_STRING_OR_ABORT(jsonDictionary, kRLMSyncIdentityKey, identity); + RLM_SYNC_PARSE_OPTIONAL_STRING(jsonDictionary, kRLMSyncAppIDKey, appID); + RLM_SYNC_PARSE_OPTIONAL_STRING(jsonDictionary, kRLMSyncPathKey, path); + RLM_SYNC_PARSE_OPTIONAL_BOOL(jsonDictionary, kRLMSyncIsAdminKey, isAdmin); + RLM_SYNC_PARSE_DOUBLE_OR_ABORT(jsonDictionary, kRLMSyncExpiresKey, expires); + return self; + } + return nil; +} + +@end + +#pragma mark - RLMTokenModel + +@interface RLMTokenModel () + +@property (nonatomic, readwrite) NSString *token; +@property (nonatomic, nullable, readwrite) NSString *path; +@property (nonatomic, readwrite) RLMTokenDataModel *tokenData; + +@end + +@implementation RLMTokenModel + +- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary { + if (self = [super init]) { + RLM_SYNC_PARSE_STRING_OR_ABORT(jsonDictionary, kRLMSyncTokenKey, token); + RLM_SYNC_PARSE_OPTIONAL_STRING(jsonDictionary, kRLMSyncPathKey, path); + RLM_SYNC_PARSE_MODEL_OR_ABORT(jsonDictionary, kRLMSyncTokenDataKey, RLMTokenDataModel, tokenData); + return self; + } + return nil; +} + +@end + +#pragma mark - RLMAuthResponseModel + +@interface RLMAuthResponseModel () + +@property (nonatomic, readwrite) RLMTokenModel *accessToken; +@property (nonatomic, readwrite) RLMTokenModel *refreshToken; + +@end + +@implementation RLMAuthResponseModel + +- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary + requireAccessToken:(BOOL)requireAccessToken + requireRefreshToken:(BOOL)requireRefreshToken { + if (self = [super init]) { + // Get the access token. + if (requireAccessToken) { + RLM_SYNC_PARSE_MODEL_OR_ABORT(jsonDictionary, kRLMSyncAccessTokenKey, RLMTokenModel, accessToken); + } else { + RLM_SYNC_PARSE_OPTIONAL_MODEL(jsonDictionary, kRLMSyncAccessTokenKey, RLMTokenModel, accessToken); + } + // Get the refresh token. + if (requireRefreshToken) { + RLM_SYNC_PARSE_MODEL_OR_ABORT(jsonDictionary, kRLMSyncRefreshTokenKey, RLMTokenModel, refreshToken); + } else { + RLM_SYNC_PARSE_OPTIONAL_MODEL(jsonDictionary, kRLMSyncRefreshTokenKey, RLMTokenModel, refreshToken); + } + return self; + } + return nil; +} + +@end + +#pragma mark - RLMUserInfoResponseModel + +@interface RLMSyncUserAccountInfo () +@property (nonatomic, readwrite) NSString *provider; +@property (nonatomic, readwrite) NSString *providerUserIdentity; +@end + +@implementation RLMSyncUserAccountInfo + +- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary { + if (self = [super init]) { + RLM_SYNC_PARSE_STRING_OR_ABORT(jsonDictionary, kRLMSyncProviderKey, provider); + RLM_SYNC_PARSE_STRING_OR_ABORT(jsonDictionary, kRLMSyncProviderIDKey, providerUserIdentity); + return self; + } + return nil; +} + +@end + +@interface RLMUserResponseModel () + +@property (nonatomic, readwrite) NSString *identity; +@property (nonatomic, readwrite) NSArray *accounts; +@property (nonatomic, readwrite) NSDictionary *metadata; +@property (nonatomic, readwrite) BOOL isAdmin; + +@end + +@implementation RLMUserResponseModel + +- (void)parseMetadataFromJSON:(NSDictionary *)jsonDictionary { + NSMutableDictionary *buffer = [NSMutableDictionary dictionary]; + NSArray *metadataArray = jsonDictionary[kRLMSyncMetadataKey]; + if (![metadataArray isKindOfClass:[NSArray class]]) { + self.metadata = @{}; + return; + } + for (NSDictionary *object in metadataArray) { + if (![object isKindOfClass:[NSDictionary class]]) { + continue; + } + NSString *key = object[kRLMSyncKeyKey]; + NSString *value = object[kRLMSyncValueKey]; + if (!key || !value) { + continue; + } + buffer[key] = value; + } + self.metadata = [buffer copy]; +} + +- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary { + if (self = [super init]) { + self.isAdmin = NO; + RLM_SYNC_PARSE_STRING_OR_ABORT(jsonDictionary, kRLMSyncUserIDKey, identity); + RLM_SYNC_PARSE_OPTIONAL_BOOL(jsonDictionary, kRLMSyncIsAdminKey, isAdmin); + RLM_SYNC_PARSE_MODEL_ARRAY_OR_ABORT(jsonDictionary, kRLMSyncAccountsKey, RLMSyncUserAccountInfo, accounts); + [self parseMetadataFromJSON:jsonDictionary]; + return self; + } + return nil; +} + +@end + +#pragma mark - RLMSyncErrorResponseModel + +@interface RLMSyncErrorResponseModel () + +@property (nonatomic, readwrite) NSInteger status; +@property (nonatomic, readwrite) NSInteger code; +@property (nonatomic, readwrite) NSString *title; +@property (nonatomic, readwrite) NSString *hint; + +@end + +@implementation RLMSyncErrorResponseModel + +- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary { + if (self = [super init]) { + RLM_SYNC_PARSE_DOUBLE_OR_ABORT(jsonDictionary, kRLMSyncErrorStatusKey, status); + RLM_SYNC_PARSE_DOUBLE_OR_ABORT(jsonDictionary, kRLMSyncErrorCodeKey, code); + RLM_SYNC_PARSE_OPTIONAL_STRING(jsonDictionary, kRLMSyncErrorTitleKey, title); + RLM_SYNC_PARSE_OPTIONAL_STRING(jsonDictionary, kRLMSyncErrorHintKey, hint); + + NSString *detail = jsonDictionary[@"detail"]; + if ([detail isKindOfClass:[NSString class]]) { + _title = detail; + } + + for (NSDictionary *problem in jsonDictionary[@"invalid_params"]) { + NSString *name = problem[@"name"]; + NSString *reason = problem[@"reason"]; + if (name && reason) { + _title = [NSString stringWithFormat:@"%@ %@: %@;", _title, name, reason]; + } + } + + return self; + } + return nil; +} + +@end diff --git a/Pods/Realm/Realm/RLMListBase.mm b/Pods/Realm/Realm/RLMListBase.mm index 78cf91a..6988acc 100644 --- a/Pods/Realm/Realm/RLMListBase.mm +++ b/Pods/Realm/Realm/RLMListBase.mm @@ -41,6 +41,10 @@ - (id)valueForKey:(NSString *)key { return [__rlmArray valueForKey:key]; } +- (id)valueForKeyPath:(NSString *)keyPath { + return [__rlmArray valueForKeyPath:keyPath]; +} + - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len { return [__rlmArray countByEnumeratingWithState:state objects:buffer count:len]; } diff --git a/Pods/Realm/Realm/RLMArrayLinkView.mm b/Pods/Realm/Realm/RLMManagedArray.mm similarity index 55% rename from Pods/Realm/Realm/RLMArrayLinkView.mm rename to Pods/Realm/Realm/RLMManagedArray.mm index d9e0696..2593618 100644 --- a/Pods/Realm/Realm/RLMArrayLinkView.mm +++ b/Pods/Realm/Realm/RLMManagedArray.mm @@ -18,6 +18,7 @@ #import "RLMArray_Private.hpp" +#import "RLMAccessor.hpp" #import "RLMObjectSchema_Private.hpp" #import "RLMObjectStore.h" #import "RLMObject_Private.hpp" @@ -31,25 +32,26 @@ #import "list.hpp" #import "results.hpp" +#import "shared_realm.hpp" #import #import -@interface RLMArrayLinkViewHandoverMetadata : NSObject +@interface RLMManagedArrayHandoverMetadata : NSObject @property (nonatomic) NSString *parentClassName; @property (nonatomic) NSString *key; @end -@implementation RLMArrayLinkViewHandoverMetadata +@implementation RLMManagedArrayHandoverMetadata @end -@interface RLMArrayLinkView () +@interface RLMManagedArray () @end // // RLMArray implementation // -@implementation RLMArrayLinkView { +@implementation RLMManagedArray { @public realm::List _backingList; RLMRealm *_realm; @@ -58,27 +60,34 @@ @implementation RLMArrayLinkView { std::unique_ptr _observationInfo; } -- (RLMArrayLinkView *)initWithList:(realm::List)list - realm:(__unsafe_unretained RLMRealm *const)realm - parentInfo:(RLMClassInfo *)parentInfo - property:(__unsafe_unretained RLMProperty *const)property { - self = [self initWithObjectClassName:property.objectClassName]; +- (RLMManagedArray *)initWithList:(realm::List)list + realm:(__unsafe_unretained RLMRealm *const)realm + parentInfo:(RLMClassInfo *)parentInfo + property:(__unsafe_unretained RLMProperty *const)property { + if (property.type == RLMPropertyTypeObject) + self = [self initWithObjectClassName:property.objectClassName]; + else + self = [self initWithObjectType:property.type optional:property.optional]; if (self) { _realm = realm; REALM_ASSERT(list.get_realm() == realm->_realm); _backingList = std::move(list); - _objectInfo = &parentInfo->linkTargetType(property.index); _ownerInfo = parentInfo; + if (property.type == RLMPropertyTypeObject) + _objectInfo = &parentInfo->linkTargetType(property.index); + else + _objectInfo = _ownerInfo; _key = property.name; } return self; } -- (RLMArrayLinkView *)initWithParent:(__unsafe_unretained RLMObjectBase *const)parentObject - property:(__unsafe_unretained RLMProperty *const)property { +- (RLMManagedArray *)initWithParent:(__unsafe_unretained RLMObjectBase *const)parentObject + property:(__unsafe_unretained RLMProperty *const)property { __unsafe_unretained RLMRealm *const realm = parentObject->_realm; - realm::List list(realm->_realm, parentObject->_row.get_linklist(parentObject->_info->tableColumn(property))); - return [self initWithList:std::move(list) + auto col = parentObject->_info->tableColumn(property); + auto& row = parentObject->_row; + return [self initWithList:realm::List(realm->_realm, *row.get_table(), col, row.get_index()) realm:realm parentInfo:parentObject->_info property:property]; @@ -97,8 +106,8 @@ void RLMEnsureArrayObservationInfo(std::unique_ptr& info, __unsafe_unretained RLMArray *const array, __unsafe_unretained id const observed) { RLMValidateArrayObservationKey(keyPath, array); - if (!info && array.class == [RLMArrayLinkView class]) { - RLMArrayLinkView *lv = static_cast(array); + if (!info && array.class == [RLMManagedArray class]) { + auto lv = static_cast(array); info = std::make_unique(*lv->_ownerInfo, lv->_backingList.get_origin_row_index(), observed); @@ -110,57 +119,65 @@ void RLMEnsureArrayObservationInfo(std::unique_ptr& info, // [[gnu::noinline]] [[noreturn]] -static void throwError() { +static void throwError(__unsafe_unretained RLMManagedArray *const ar, NSString *aggregateMethod) { try { throw; } catch (realm::InvalidTransactionException const&) { - @throw RLMException(@"Cannot modify managed RLMArray outside of a write transaction"); + @throw RLMException(@"Cannot modify managed RLMArray outside of a write transaction."); } catch (realm::IncorrectThreadException const&) { - @throw RLMException(@"Realm accessed from incorrect thread"); + @throw RLMException(@"Realm accessed from incorrect thread."); } catch (realm::List::InvalidatedException const&) { - @throw RLMException(@"RLMArray has been invalidated or the containing object has been deleted"); + @throw RLMException(@"RLMArray has been invalidated or the containing object has been deleted."); } catch (realm::List::OutOfBoundsIndexException const& e) { - @throw RLMException(@"Index %zu is out of bounds (must be less than %zu)", + @throw RLMException(@"Index %zu is out of bounds (must be less than %zu).", e.requested, e.valid_count); } + catch (realm::Results::UnsupportedColumnTypeException const& e) { + if (ar->_backingList.get_type() == realm::PropertyType::Object) { + @throw RLMException(@"%@: is not supported for %s%s property '%s'.", + aggregateMethod, + string_for_property_type(e.property_type), + is_nullable(e.property_type) ? "?" : "", + e.column_name.data()); + } + @throw RLMException(@"%@: is not supported for %s%s array '%@.%@'.", + aggregateMethod, + string_for_property_type(e.property_type), + is_nullable(e.property_type) ? "?" : "", + ar->_ownerInfo->rlmObjectSchema.className, ar->_key); + } + catch (std::logic_error const& e) { + @throw RLMException(e); + } } template -static auto translateErrors(Function&& f) { +static auto translateErrors(__unsafe_unretained RLMManagedArray *const ar, + Function&& f, NSString *aggregateMethod=nil) { try { return f(); } catch (...) { - throwError(); + throwError(ar, aggregateMethod); } } -static void validateObjectToAdd(__unsafe_unretained RLMArrayLinkView *const ar, - __unsafe_unretained RLMObject *const obj) { - if (!obj) { - @throw RLMException(@"Cannot add `nil` to RLMArray<%@>", ar->_objectClassName); - } - - NSString *objectClassName = obj->_objectSchema.className; - if (![objectClassName isEqualToString:ar->_objectClassName]) { - @throw RLMException(@"Cannot add object of type '%@' to RLMArray<%@>", - objectClassName, ar->_objectClassName); - } - - if (obj->_realm != ar.realm) { - [ar.realm addObject:obj]; +template +static auto translateErrors(Function&& f) { + try { + return f(); } - else if (obj->_realm && !obj->_row.is_attached()) { - @throw RLMException(@"Object has been deleted or invalidated."); + catch (...) { + throwError(nil, nil); } } template -static void changeArray(__unsafe_unretained RLMArrayLinkView *const ar, +static void changeArray(__unsafe_unretained RLMManagedArray *const ar, NSKeyValueChange kind, dispatch_block_t f, IndexSetFactory&& is) { translateErrors([&] { ar->_backingList.verify_in_transaction(); }); RLMObservationInfo *info = RLMGetObservationInfo(ar->_observationInfo.get(), @@ -174,7 +191,7 @@ static void changeArray(__unsafe_unretained RLMArrayLinkView *const ar, } catch (...) { info->didChange(ar->_key, kind, indexes); - throwError(); + throwError(ar, nil); } info->didChange(ar->_key, kind, indexes); } @@ -183,15 +200,15 @@ static void changeArray(__unsafe_unretained RLMArrayLinkView *const ar, } } -static void changeArray(__unsafe_unretained RLMArrayLinkView *const ar, NSKeyValueChange kind, NSUInteger index, dispatch_block_t f) { +static void changeArray(__unsafe_unretained RLMManagedArray *const ar, NSKeyValueChange kind, NSUInteger index, dispatch_block_t f) { changeArray(ar, kind, f, [=] { return [NSIndexSet indexSetWithIndex:index]; }); } -static void changeArray(__unsafe_unretained RLMArrayLinkView *const ar, NSKeyValueChange kind, NSRange range, dispatch_block_t f) { +static void changeArray(__unsafe_unretained RLMManagedArray *const ar, NSKeyValueChange kind, NSRange range, dispatch_block_t f) { changeArray(ar, kind, f, [=] { return [NSIndexSet indexSetWithIndexesInRange:range]; }); } -static void changeArray(__unsafe_unretained RLMArrayLinkView *const ar, NSKeyValueChange kind, NSIndexSet *is, dispatch_block_t f) { +static void changeArray(__unsafe_unretained RLMManagedArray *const ar, NSKeyValueChange kind, NSIndexSet *is, dispatch_block_t f) { changeArray(ar, kind, f, [=] { return is; }); } @@ -214,11 +231,13 @@ - (RLMClassInfo *)objectInfo { return _objectInfo; } + +- (bool)isBackedByList:(realm::List const&)list { + return _backingList == list; +} + - (BOOL)isEqual:(id)object { - if (RLMArrayLinkView *linkView = RLMDynamicCast(object)) { - return linkView->_backingList == _backingList; - } - return NO; + return [object respondsToSelector:@selector(isBackedByList:)] && [object isBackedByList:_backingList]; } - (NSUInteger)hash { @@ -228,51 +247,43 @@ - (NSUInteger)hash { - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(__unused __unsafe_unretained id [])buffer count:(NSUInteger)len { - __autoreleasing RLMFastEnumerator *enumerator; - if (state->state == 0) { - translateErrors([&] { _backingList.verify_attached(); }); - - enumerator = [[RLMFastEnumerator alloc] initWithCollection:self objectSchema:*_objectInfo]; - state->extra[0] = (long)enumerator; - state->extra[1] = self.count; - } - else { - enumerator = (__bridge id)(void *)state->extra[0]; - } - - return [enumerator countByEnumeratingWithState:state count:len]; + return RLMFastEnumerate(state, len, self); } - (id)objectAtIndex:(NSUInteger)index { - return RLMCreateObjectAccessor(_realm, *_objectInfo, - translateErrors([&] { return _backingList.get(index).get_index(); })); + return translateErrors([&] { + RLMAccessorContext context(_realm, *_objectInfo); + return _backingList.get(context, index); + }); } -static void RLMInsertObject(RLMArrayLinkView *ar, RLMObject *object, NSUInteger index) { +static void RLMInsertObject(RLMManagedArray *ar, id object, NSUInteger index) { + RLMArrayValidateMatchingObjectType(ar, object); if (index == NSUIntegerMax) { index = translateErrors([&] { return ar->_backingList.size(); }); } - validateObjectToAdd(ar, object); changeArray(ar, NSKeyValueChangeInsertion, index, ^{ - ar->_backingList.insert(index, object->_row.get_index()); + RLMAccessorContext context(ar->_realm, *ar->_objectInfo); + ar->_backingList.insert(context, index, object); }); } -- (void)addObject:(RLMObject *)object { +- (void)addObject:(id)object { RLMInsertObject(self, object, NSUIntegerMax); } -- (void)insertObject:(RLMObject *)object atIndex:(NSUInteger)index { +- (void)insertObject:(id)object atIndex:(NSUInteger)index { RLMInsertObject(self, object, index); } - (void)insertObjects:(id)objects atIndexes:(NSIndexSet *)indexes { changeArray(self, NSKeyValueChangeInsertion, indexes, ^{ NSUInteger index = [indexes firstIndex]; - for (RLMObject *obj in objects) { - validateObjectToAdd(self, obj); - _backingList.insert(index, obj->_row.get_index()); + RLMAccessorContext context(_realm, *_objectInfo); + for (id obj in objects) { + RLMArrayValidateMatchingObjectType(self, obj); + _backingList.insert(context, index, obj); index = [indexes indexGreaterThanIndex:index]; } }); @@ -295,9 +306,10 @@ - (void)removeObjectsAtIndexes:(NSIndexSet *)indexes { - (void)addObjectsFromArray:(NSArray *)array { changeArray(self, NSKeyValueChangeInsertion, NSMakeRange(self.count, array.count), ^{ - for (RLMObject *obj in array) { - validateObjectToAdd(self, obj); - _backingList.add(obj->_row.get_index()); + RLMAccessorContext context(_realm, *_objectInfo); + for (id obj in array) { + RLMArrayValidateMatchingObjectType(self, obj); + _backingList.add(context, obj); } }); } @@ -308,10 +320,11 @@ - (void)removeAllObjects { }); } -- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(RLMObject *)object { - validateObjectToAdd(self, object); +- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)object { + RLMArrayValidateMatchingObjectType(self, object); changeArray(self, NSKeyValueChangeReplacement, index, ^{ - _backingList.set(index, object->_row.get_index()); + RLMAccessorContext context(_realm, *_objectInfo); + _backingList.set(context, index, object); }); } @@ -333,27 +346,21 @@ - (void)exchangeObjectAtIndex:(NSUInteger)index1 withObjectAtIndex:(NSUInteger)i }); } -- (NSUInteger)indexOfObject:(RLMObject *)object { - if (object.invalidated) { - @throw RLMException(@"Object has been deleted or invalidated"); - } - - // check that object types align - if (![_objectClassName isEqualToString:object->_objectSchema.className]) { - @throw RLMException(@"Object of type (%@) does not match RLMArray type (%@)", - object->_objectSchema.className, _objectClassName); - } - - return translateErrors([&] { return RLMConvertNotFound(_backingList.find(object->_row)); }); +- (NSUInteger)indexOfObject:(id)object { + RLMArrayValidateMatchingObjectType(self, object); + return translateErrors([&] { + RLMAccessorContext context(_realm, *_objectInfo); + return RLMConvertNotFound(_backingList.find(context, object)); + }); } - (id)valueForKeyPath:(NSString *)keyPath { if ([keyPath hasPrefix:@"@"]) { // Delegate KVC collection operators to RLMResults - auto query = translateErrors([&] { return _backingList.get_query(); }); - RLMResults *results = [RLMResults resultsWithObjectInfo:*_objectInfo - results:realm::Results(_realm->_realm, std::move(query))]; - return [results valueForKeyPath:keyPath]; + return translateErrors([&] { + auto results = [RLMResults resultsWithObjectInfo:*_objectInfo results:_backingList.as_results()]; + return [results valueForKeyPath:keyPath]; + }); } return [super valueForKeyPath:keyPath]; } @@ -363,22 +370,76 @@ - (id)valueForKey:(NSString *)key { // normal array KVC semantics, but observing @things works very oddly (when // it's part of a key path, it's triggered automatically when array index // changes occur, and can't be sent explicitly, but works normally when it's - // the entire key path), and an RLMArrayLinkView *can't* have objects where + // the entire key path), and an RLMManagedArray *can't* have objects where // invalidated is true, so we're not losing much. - if ([key isEqualToString:RLMInvalidatedKey]) { - return @(!_backingList.is_valid()); - } + return translateErrors([&]() -> id { + if ([key isEqualToString:RLMInvalidatedKey]) { + return @(!_backingList.is_valid()); + } - translateErrors([&] { _backingList.verify_attached(); }); - return RLMCollectionValueForKey(self, key); + _backingList.verify_attached(); + return RLMCollectionValueForKey(_backingList, key, _realm, *_objectInfo); + }); } - (void)setValue:(id)value forKey:(NSString *)key { - translateErrors([&] { _backingList.verify_in_transaction(); }); - RLMCollectionSetValueForKey(self, key, value); + if ([key isEqualToString:@"self"]) { + RLMArrayValidateMatchingObjectType(self, value); + RLMAccessorContext context(_realm, *_objectInfo); + translateErrors([&] { + for (size_t i = 0, count = _backingList.size(); i < count; ++i) { + _backingList.set(context, i, value); + } + }); + return; + } + else if (_type == RLMPropertyTypeObject) { + RLMArrayValidateMatchingObjectType(self, value); + translateErrors([&] { _backingList.verify_in_transaction(); }); + RLMCollectionSetValueForKey(self, key, value); + } + else { + [self setValue:value forUndefinedKey:key]; + } +} + +- (size_t)columnForProperty:(NSString *)propertyName { + if (_backingList.get_type() == realm::PropertyType::Object) { + return _objectInfo->tableColumn(propertyName); + } + if (![propertyName isEqualToString:@"self"]) { + @throw RLMException(@"Arrays of '%@' can only be aggregated on \"self\"", RLMTypeToString(_type)); + } + return 0; +} + +- (id)minOfProperty:(NSString *)property { + size_t column = [self columnForProperty:property]; + auto value = translateErrors(self, [&] { return _backingList.min(column); }, @"minOfProperty"); + return value ? RLMMixedToObjc(*value) : nil; +} + +- (id)maxOfProperty:(NSString *)property { + size_t column = [self columnForProperty:property]; + auto value = translateErrors(self, [&] { return _backingList.max(column); }, @"maxOfProperty"); + return value ? RLMMixedToObjc(*value) : nil; +} + +- (id)sumOfProperty:(NSString *)property { + size_t column = [self columnForProperty:property]; + return RLMMixedToObjc(translateErrors(self, [&] { return _backingList.sum(column); }, @"sumOfProperty")); +} + +- (id)averageOfProperty:(NSString *)property { + size_t column = [self columnForProperty:property]; + auto value = translateErrors(self, [&] { return _backingList.average(column); }, @"averageOfProperty"); + return value ? @(*value) : nil; } - (void)deleteObjectsFromRealm { + if (_type != RLMPropertyTypeObject) { + @throw RLMException(@"Cannot delete objects from RLMArray<%@>: only RLMObjects can be deleted.", RLMTypeToString(_type)); + } // delete all target rows from the realm RLMTrackDeletions(_realm, ^{ translateErrors([&] { _backingList.delete_all(); }); @@ -386,35 +447,31 @@ - (void)deleteObjectsFromRealm { } - (RLMResults *)sortedResultsUsingDescriptors:(NSArray *)properties { - if (properties.count == 0) { - auto results = translateErrors([&] { return _backingList.filter({}); }); - return [RLMResults resultsWithObjectInfo:*_objectInfo results:std::move(results)]; - } - - auto order = RLMSortDescriptorFromDescriptors(*_objectInfo, properties); - auto results = translateErrors([&] { return _backingList.sort(std::move(order)); }); - return [RLMResults resultsWithObjectInfo:*_objectInfo results:std::move(results)]; + return translateErrors([&] { + return [RLMResults resultsWithObjectInfo:*_objectInfo + results:_backingList.sort(RLMSortDescriptorsToKeypathArray(properties))]; + }); } - (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate { + if (_type != RLMPropertyTypeObject) { + @throw RLMException(@"Querying is currently only implemented for arrays of Realm Objects"); + } auto query = RLMPredicateToQuery(predicate, _objectInfo->rlmObjectSchema, _realm.schema, _realm.group); auto results = translateErrors([&] { return _backingList.filter(std::move(query)); }); return [RLMResults resultsWithObjectInfo:*_objectInfo results:std::move(results)]; } - (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate { - auto query = translateErrors([&] { return _backingList.get_query(); }); - query.and_query(RLMPredicateToQuery(predicate, _objectInfo->rlmObjectSchema, _realm.schema, _realm.group)); -#if REALM_VER_MAJOR >= 2 - auto indexInTable = query.find(); - if (indexInTable == realm::not_found) { - return NSNotFound; + if (_type != RLMPropertyTypeObject) { + @throw RLMException(@"Querying is currently only implemented for arrays of Realm Objects"); } - auto row = query.get_table()->get(indexInTable); - return _backingList.find(row); -#else - return RLMConvertNotFound(query.find()); -#endif + realm::Query query = RLMPredicateToQuery(predicate, _objectInfo->rlmObjectSchema, + _realm.schema, _realm.group); + + return translateErrors([&] { + return RLMConvertNotFound(_backingList.find(std::move(query))); + }); } - (NSArray *)objectsAtIndexes:(__unused NSIndexSet *)indexes { @@ -431,14 +488,17 @@ - (void)addObserver:(id)observer [super addObserver:observer forKeyPath:keyPath options:options context:context]; } -- (NSUInteger)indexInSource:(NSUInteger)index { - return _backingList.get_unchecked(index); -} - - (realm::TableView)tableView { return translateErrors([&] { return _backingList.get_query(); }).find_all(); } +- (RLMFastEnumerator *)fastEnumerator { + return translateErrors([&] { + return [[RLMFastEnumerator alloc] initWithList:_backingList collection:self + realm:_realm classInfo:*_objectInfo]; + }); +} + // The compiler complains about the method's argument type not matching due to // it not having the generic type attached, but it doesn't seem to be possible // to actually include the generic type @@ -446,7 +506,7 @@ - (NSUInteger)indexInSource:(NSUInteger)index { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmismatched-parameter-types" - (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMArray *, RLMCollectionChange *, NSError *))block { - [_realm verifyNotificationsAreSupported]; + [_realm verifyNotificationsAreSupported:true]; return RLMAddNotificationBlock(self, _backingList, block); } #pragma clang diagnostic pop @@ -454,29 +514,29 @@ - (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMArray *, RLMCollecti #pragma mark - Thread Confined Protocol Conformance - (std::unique_ptr)makeThreadSafeReference { - realm::ThreadSafeReference list_reference = _realm->_realm->obtain_thread_safe_reference(_backingList); + auto list_reference = _realm->_realm->obtain_thread_safe_reference(_backingList); return std::make_unique>(std::move(list_reference)); } -- (RLMArrayLinkViewHandoverMetadata *)objectiveCMetadata { - RLMArrayLinkViewHandoverMetadata *metadata = [[RLMArrayLinkViewHandoverMetadata alloc] init]; +- (RLMManagedArrayHandoverMetadata *)objectiveCMetadata { + RLMManagedArrayHandoverMetadata *metadata = [[RLMManagedArrayHandoverMetadata alloc] init]; metadata.parentClassName = _ownerInfo->rlmObjectSchema.className; metadata.key = _key; return metadata; } + (instancetype)objectWithThreadSafeReference:(std::unique_ptr)reference - metadata:(RLMArrayLinkViewHandoverMetadata *)metadata + metadata:(RLMManagedArrayHandoverMetadata *)metadata realm:(RLMRealm *)realm { REALM_ASSERT_DEBUG(dynamic_cast *>(reference.get())); auto list_reference = static_cast *>(reference.get()); - realm::List list = realm->_realm->resolve_thread_safe_reference(std::move(*list_reference)); + auto list = realm->_realm->resolve_thread_safe_reference(std::move(*list_reference)); if (!list.is_valid()) { return nil; } RLMClassInfo *parentInfo = &realm->_info[metadata.parentClassName]; - return [[RLMArrayLinkView alloc] initWithList:std::move(list) + return [[RLMManagedArray alloc] initWithList:std::move(list) realm:realm parentInfo:parentInfo property:parentInfo->rlmObjectSchema[metadata.key]]; diff --git a/Pods/Realm/Realm/RLMMigration.mm b/Pods/Realm/Realm/RLMMigration.mm index 6d8da01..6c0c571 100644 --- a/Pods/Realm/Realm/RLMMigration.mm +++ b/Pods/Realm/Realm/RLMMigration.mm @@ -20,12 +20,13 @@ #import "RLMAccessor.h" #import "RLMObject_Private.h" +#import "RLMObject_Private.hpp" #import "RLMObjectSchema_Private.hpp" #import "RLMObjectStore.h" #import "RLMProperty_Private.h" #import "RLMRealm_Dynamic.h" #import "RLMRealm_Private.hpp" -#import "RLMResults_Private.h" +#import "RLMResults_Private.hpp" #import "RLMSchema_Private.hpp" #import "RLMUtil.hpp" @@ -55,6 +56,7 @@ - (void)beginWriteTransaction { @implementation RLMMigration { realm::Schema *_schema; + std::unordered_map _deletedObjectIndices; } - (instancetype)initWithRealm:(RLMRealm *)realm oldRealm:(RLMRealm *)oldRealm schema:(realm::Schema &)schema { @@ -76,30 +78,35 @@ - (RLMSchema *)newSchema { return self.realm.schema; } -- (void)enumerateObjects:(NSString *)className block:(RLMObjectMigrationBlock)block { - // get all objects +- (void)enumerateObjects:(NSString *)className block:(__attribute__((noescape)) RLMObjectMigrationBlock)block { RLMResults *objects = [_realm.schema schemaForClassName:className] ? [_realm allObjects:className] : nil; RLMResults *oldObjects = [_oldRealm.schema schemaForClassName:className] ? [_oldRealm allObjects:className] : nil; - if (objects && oldObjects) { - for (long i = oldObjects.count - 1; i >= 0; i--) { + // For whatever reason if this is a newly added table we enumerate the + // objects in it, while in all other cases we enumerate only the existing + // objects. It's unclear how this could be useful, but changing it would + // also be a pointless breaking change and it's unlikely to be hurting anyone. + if (objects && !oldObjects) { + for (auto i = objects.count; i > 0; --i) { @autoreleasepool { - block(oldObjects[i], objects[i]); + block(nil, objects[i - 1]); } } + return; } - else if (objects) { - for (long i = objects.count - 1; i >= 0; i--) { - @autoreleasepool { - block(nil, objects[i]); - } - } + + auto count = oldObjects.count; + if (count == 0) { + return; } - else if (oldObjects) { - for (long i = oldObjects.count - 1; i >= 0; i--) { - @autoreleasepool { - block(oldObjects[i], nil); - } + auto deletedObjects = _deletedObjectIndices.find(className); + for (auto i = count; i > 0; --i) { + auto index = i - 1; + if (deletedObjects != _deletedObjectIndices.end() && deletedObjects->second.contains(index)) { + continue; + } + @autoreleasepool { + block(oldObjects[index], objects[index]); } } } @@ -117,6 +124,8 @@ - (void)execute:(RLMMigrationBlock)block { block(self, _oldRealm->_realm->schema_version()); + [self deleteObjectsMarkedForDeletion]; + _oldRealm = nil; _realm = nil; } @@ -131,7 +140,31 @@ - (RLMObject *)createObject:(NSString *)className withObject:(id)object { } - (void)deleteObject:(RLMObject *)object { - [_realm deleteObject:object]; + _deletedObjectIndices[object.objectSchema.className].add(object->_row.get_index()); +} + +- (void)deleteObjectsMarkedForDeletion { + for (auto& objectType : _deletedObjectIndices) { + TableRef table = ObjectStore::table_for_object_type(_realm.group, objectType.first.UTF8String); + if (!table) { + continue; + } + + auto& indices = objectType.second; + // Just clear the table if we're removing all of the rows + if (table->size() == indices.count()) { + table->clear(); + } + // Otherwise delete in reverse order to avoid invaliding any of the + // not-yet-deleted indices + else { + for (auto it = std::make_reverse_iterator(indices.end()), end = std::make_reverse_iterator(indices.begin()); it != end; ++it) { + for (size_t i = it->second; i > it->first; --i) { + table->move_last_over(i - 1); + } + } + } + } } - (BOOL)deleteDataForClassName:(NSString *)name { @@ -143,11 +176,8 @@ - (BOOL)deleteDataForClassName:(NSString *)name { if (!table) { return false; } - - if ([_realm.schema schemaForClassName:name]) { - table->clear(); - } - else { + _deletedObjectIndices[name].set(table->size()); + if (![_realm.schema schemaForClassName:name]) { realm::ObjectStore::delete_data_for_object(_realm.group, name.UTF8String); } @@ -155,8 +185,8 @@ - (BOOL)deleteDataForClassName:(NSString *)name { } - (void)renamePropertyForClass:(NSString *)className oldName:(NSString *)oldName newName:(NSString *)newName { - const char *objectType = className.UTF8String; - realm::ObjectStore::rename_property(_realm.group, *_schema, objectType, oldName.UTF8String, newName.UTF8String); + realm::ObjectStore::rename_property(_realm.group, *_schema, className.UTF8String, + oldName.UTF8String, newName.UTF8String); } @end diff --git a/Pods/Realm/Realm/RLMNetworkClient.m b/Pods/Realm/Realm/RLMNetworkClient.m deleted file mode 100644 index ca8a156..0000000 --- a/Pods/Realm/Realm/RLMNetworkClient.m +++ /dev/null @@ -1,229 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#import "RLMNetworkClient.h" - -#import "RLMRealmConfiguration.h" -#import "RLMSyncErrorResponseModel.h" -#import "RLMSyncUtil_Private.h" - -typedef void(^RLMServerURLSessionCompletionBlock)(NSData *, NSURLResponse *, NSError *); - -static NSUInteger const kHTTPCodeRange = 100; - -typedef enum : NSUInteger { - Informational = 1, // 1XX - Success = 2, // 2XX - Redirection = 3, // 3XX - ClientError = 4, // 4XX - ServerError = 5, // 5XX -} RLMServerHTTPErrorCodeType; - -static NSRange RLM_rangeForErrorType(RLMServerHTTPErrorCodeType type) { - return NSMakeRange(type*100, kHTTPCodeRange); -} - -@implementation RLMNetworkClient - -+ (NSURLSession *)session { - return [NSURLSession sharedSession]; -} - -+ (NSURL *)urlForServer:(NSURL *)serverURL endpoint:(RLMServerEndpoint)endpoint { - NSString *pathComponent = nil; - switch (endpoint) { - case RLMServerEndpointAuth: - pathComponent = @"auth"; - break; - case RLMServerEndpointLogout: - // TODO: fix this - pathComponent = @"logout"; - NSAssert(NO, @"logout endpoint isn't implemented yet, don't use it"); - break; - case RLMServerEndpointAddCredentials: - // TODO: fix this - pathComponent = @"addCredentials"; - NSAssert(NO, @"add credentials endpoint isn't implemented yet, don't use it"); - break; - case RLMServerEndpointRemoveCredentials: - // TODO: fix this - pathComponent = @"removeCredentials"; - NSAssert(NO, @"remove credentials endpoint isn't implemented yet, don't use it"); - break; - } - NSAssert(pathComponent != nil, @"Unrecognized value for RLMServerEndpoint enum"); - return [serverURL URLByAppendingPathComponent:pathComponent]; -} - -+ (void)postRequestToEndpoint:(RLMServerEndpoint)endpoint - server:(NSURL *)serverURL - JSON:(NSDictionary *)jsonDictionary - completion:(RLMSyncCompletionBlock)completionBlock { - static NSTimeInterval const defaultTimeout = 60; - [self postRequestToEndpoint:endpoint - server:serverURL - JSON:jsonDictionary - timeout:defaultTimeout - completion:completionBlock]; -} - -// FIXME: should completion argument also pass back the NSURLResponse and/or the raw data? -+ (void)postRequestToEndpoint:(RLMServerEndpoint)endpoint - server:(NSURL *)serverURL - JSON:(NSDictionary *)jsonDictionary - timeout:(NSTimeInterval)timeout - completion:(RLMSyncCompletionBlock)completionBlock { - - NSError *localError = nil; - - // Attempt to convert the JSON - NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonDictionary - options:(NSJSONWritingOptions)0 - error:&localError]; - if (!jsonData) { - completionBlock(localError, nil); - return; - } - - // Create the request - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[self urlForServer:serverURL endpoint:endpoint]]; - request.HTTPBody = jsonData; - request.HTTPMethod = @"POST"; - request.timeoutInterval = MAX(timeout, 10); - [request addValue:@"application/json;charset=utf-8" forHTTPHeaderField:@"Content-Type"]; - [request addValue:@"application/json" forHTTPHeaderField:@"Accept"]; - - RLMServerURLSessionCompletionBlock handler = ^(NSData *data, - NSURLResponse *response, - NSError *error) { - if (error != nil) { - // Network error - completionBlock(error, nil); - return; - } - - NSError *localError = nil; - - if (![self validateResponse:response data:data error:&localError]) { - // Response error - completionBlock(localError, nil); - return; - } - - // Parse out the JSON - id json = [NSJSONSerialization JSONObjectWithData:data - options:(NSJSONReadingOptions)0 - error:&localError]; - if (!json || localError) { - // JSON parsing error - completionBlock(localError, nil); - } else if (![json isKindOfClass:[NSDictionary class]]) { - // JSON response malformed - localError = [NSError errorWithDomain:RLMSyncErrorDomain - code:RLMSyncErrorBadResponse - userInfo:@{kRLMSyncErrorJSONKey: json}]; - completionBlock(localError, nil); - } else { - // JSON parsed successfully - completionBlock(nil, (NSDictionary *)json); - } - }; - - // Add the request to a task and start it - NSURLSessionTask *task = [self.session dataTaskWithRequest:request - completionHandler:handler]; - [task resume]; -} - -+ (BOOL)validateResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError * __autoreleasing *)error { - __autoreleasing NSError *localError = nil; - if (!error) { - error = &localError; - } - - if (![response isKindOfClass:[NSHTTPURLResponse class]]) { - // FIXME: Provide error message - *error = [NSError errorWithDomain:RLMSyncErrorDomain code:RLMSyncErrorBadResponse userInfo:nil]; - return NO; - } - - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; - BOOL badResponse = (NSLocationInRange(httpResponse.statusCode, RLM_rangeForErrorType(ClientError)) - || NSLocationInRange(httpResponse.statusCode, RLM_rangeForErrorType(ServerError))); - if (badResponse) { - NSError *responseError = [self errorFromResponseData:data]; - if (responseError && responseError.userInfo[kRLMSyncErrorStatusCodeKey]) { - switch (responseError.code) { - case RLMSyncAuthErrorInvalidCredential: - case RLMSyncAuthErrorUserDoesNotExist: - case RLMSyncAuthErrorUserAlreadyExists: - // Authentication error - *error = responseError; - break; - - default: - // HTTP status error with some additional infor from the server - *error = [NSError errorWithDomain:RLMSyncErrorDomain - code:RLMSyncErrorHTTPStatusCodeError - userInfo:responseError.userInfo]; - break; - } - } else { - // Fallback to HTTP status error without any additional info - *error = [NSError errorWithDomain:RLMSyncErrorDomain - code:RLMSyncErrorHTTPStatusCodeError - userInfo:@{kRLMSyncErrorStatusCodeKey: @(httpResponse.statusCode)}]; - } - - return NO; - } - - if (!data) { - // FIXME: provide error message - *error = [NSError errorWithDomain:RLMSyncErrorDomain code:RLMSyncErrorBadResponse userInfo:nil]; - return NO; - } - - return YES; -} - -+ (NSError *)errorFromResponseData:(NSData *)data { - if (data.length == 0) { - return nil; - } - - id json = [NSJSONSerialization JSONObjectWithData:data - options:(NSJSONReadingOptions)0 - error:nil]; - if (!json || ![json isKindOfClass:[NSDictionary class]]) { - return nil; - } - - RLMSyncErrorResponseModel *responseModel = [[RLMSyncErrorResponseModel alloc] initWithDictionary:json]; - if (!responseModel) { - return nil; - } - - NSMutableDictionary *mutableUserInfo = [NSMutableDictionary dictionaryWithObject:@(responseModel.status) forKey:kRLMSyncErrorStatusCodeKey]; - [mutableUserInfo setValue:responseModel.title forKey:NSLocalizedDescriptionKey]; - [mutableUserInfo setValue:responseModel.hint forKey:NSLocalizedRecoverySuggestionErrorKey]; - - return [NSError errorWithDomain:RLMSyncErrorDomain code:responseModel.code userInfo:mutableUserInfo]; -} - -@end diff --git a/Pods/Realm/Realm/RLMNetworkClient.mm b/Pods/Realm/Realm/RLMNetworkClient.mm new file mode 100644 index 0000000..e2e5c16 --- /dev/null +++ b/Pods/Realm/Realm/RLMNetworkClient.mm @@ -0,0 +1,326 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMNetworkClient.h" + +#import "RLMRealmConfiguration.h" +#import "RLMJSONModels.h" +#import "RLMSyncUtil_Private.hpp" +#import "RLMUtil.hpp" + +typedef void(^RLMServerURLSessionCompletionBlock)(NSData *, NSURLResponse *, NSError *); + +static NSUInteger const kHTTPCodeRange = 100; + +typedef enum : NSUInteger { + Informational = 1, // 1XX + Success = 2, // 2XX + Redirection = 3, // 3XX + ClientError = 4, // 4XX + ServerError = 5, // 5XX +} RLMServerHTTPErrorCodeType; + +static NSRange rangeForErrorType(RLMServerHTTPErrorCodeType type) { + return NSMakeRange(type*100, kHTTPCodeRange); +} + +static std::atomic g_defaultTimeout{60.0}; + +@interface RLMSyncServerEndpoint () +- (instancetype)initPrivate NS_DESIGNATED_INITIALIZER; + +/// The HTTP method the endpoint expects. Defaults to POST. +- (NSString *)httpMethod; + +/// The URL to which the request should be made. Must be implemented. +- (NSURL *)urlForAuthServer:(NSURL *)authServerURL payload:(NSDictionary *)json; + +/// The body for the request, if any. +- (NSData *)httpBodyForPayload:(NSDictionary *)json error:(NSError **)error; + +/// The HTTP headers to be added to the request, if any. +- (NSDictionary *)httpHeadersForPayload:(NSDictionary *)json + options:(nullable RLMNetworkRequestOptions *)options; +@end + +@implementation RLMSyncServerEndpoint + ++ (void)sendRequestToServer:(NSURL *)serverURL + JSON:(NSDictionary *)jsonDictionary + options:(nullable RLMNetworkRequestOptions *)options + completion:(void (^)(NSError *))completionBlock { + [RLMNetworkClient sendRequestToEndpoint:[self endpoint] + server:serverURL + JSON:jsonDictionary + timeout:g_defaultTimeout.load() + options:options + completion:^(NSError *error, NSDictionary *) { + completionBlock(error); + }]; +} + ++ (instancetype)endpoint { + return [[self alloc] initPrivate]; +} + +- (instancetype)initPrivate { + return (self = [super init]); +} + +- (NSString *)httpMethod { + return @"POST"; +} + +- (NSURL *)urlForAuthServer:(__unused NSURL *)authServerURL payload:(__unused NSDictionary *)json { + NSAssert(NO, @"This method must be overriden by concrete subclasses."); + return nil; +} + +- (NSData *)httpBodyForPayload:(NSDictionary *)json error:(NSError **)error { + NSError *localError = nil; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:json + options:(NSJSONWritingOptions)0 + error:&localError]; + if (jsonData && !localError) { + return jsonData; + } + NSAssert(localError, @"If there isn't a converted data object there must be an error."); + if (error) { + *error = localError; + } + return nil; +} + +- (NSDictionary *)httpHeadersForPayload:(__unused NSDictionary *)json options:(nullable RLMNetworkRequestOptions *)options { + NSMutableDictionary *headers = [[NSMutableDictionary alloc] init]; + headers[@"Content-Type"] = @"application/json;charset=utf-8"; + headers[@"Accept"] = @"application/json"; + + if (NSDictionary *customHeaders = options.customHeaders) { + [headers addEntriesFromDictionary:customHeaders]; + } + + return headers; +} +@end + +@implementation RLMSyncAuthEndpoint +- (NSURL *)urlForAuthServer:(NSURL *)authServerURL payload:(__unused NSDictionary *)json { + return [authServerURL URLByAppendingPathComponent:@"auth"]; +} +@end + +@implementation RLMSyncChangePasswordEndpoint +- (NSString *)httpMethod { + return @"PUT"; +} + +- (NSURL *)urlForAuthServer:(NSURL *)authServerURL payload:(__unused NSDictionary *)json { + return [authServerURL URLByAppendingPathComponent:@"auth/password"]; +} + +- (NSDictionary *)httpHeadersForPayload:(NSDictionary *)json options:(nullable RLMNetworkRequestOptions *)options { + NSString *authToken = [json objectForKey:kRLMSyncTokenKey]; + if (!authToken) { + @throw RLMException(@"Malformed request; this indicates an internal error."); + } + NSMutableDictionary *headers = [[super httpHeadersForPayload:json options:options] mutableCopy]; + headers[options.authorizationHeaderName ?: @"Authorization"] = authToken; + return headers; +} +@end + +@implementation RLMSyncUpdateAccountEndpoint +- (NSURL *)urlForAuthServer:(NSURL *)authServerURL payload:(__unused NSDictionary *)json { + return [authServerURL URLByAppendingPathComponent:@"auth/password/updateAccount"]; +} +@end + +@implementation RLMSyncGetUserInfoEndpoint +- (NSString *)httpMethod { + return @"GET"; +} + +- (NSURL *)urlForAuthServer:(NSURL *)authServerURL payload:(NSDictionary *)json { + NSString *provider = json[kRLMSyncProviderKey]; + NSString *providerID = json[kRLMSyncProviderIDKey]; + NSAssert([provider isKindOfClass:[NSString class]] && [providerID isKindOfClass:[NSString class]], + @"malformed request; this indicates a logic error in the binding."); + NSCharacterSet *allowed = [NSCharacterSet URLQueryAllowedCharacterSet]; + NSString *pathComponent = [NSString stringWithFormat:@"auth/users/%@/%@", + [provider stringByAddingPercentEncodingWithAllowedCharacters:allowed], + [providerID stringByAddingPercentEncodingWithAllowedCharacters:allowed]]; + return [authServerURL URLByAppendingPathComponent:pathComponent]; +} + +- (NSData *)httpBodyForPayload:(__unused NSDictionary *)json error:(__unused NSError **)error { + return nil; +} + +- (NSDictionary *)httpHeadersForPayload:(NSDictionary *)json options:(nullable RLMNetworkRequestOptions *)options { + NSString *authToken = [json objectForKey:kRLMSyncTokenKey]; + if (!authToken) { + @throw RLMException(@"Malformed request; this indicates an internal error."); + } + NSMutableDictionary *headers = [[super httpHeadersForPayload:json options:options] mutableCopy]; + headers[options.authorizationHeaderName ?: @"Authorization"] = authToken; + return headers; +} +@end + + +@implementation RLMNetworkClient ++ (void)setDefaultTimeout:(NSTimeInterval)timeOut { + g_defaultTimeout = timeOut; +} + ++ (NSURLSession *)session { + return [NSURLSession sharedSession]; +} + ++ (void)sendRequestToEndpoint:(RLMSyncServerEndpoint *)endpoint + server:(NSURL *)serverURL + JSON:(NSDictionary *)jsonDictionary + timeout:(NSTimeInterval)timeout + options:(nullable RLMNetworkRequestOptions *)options + completion:(RLMSyncCompletionBlock)completionBlock { + // Create the request + NSError *localError = nil; + NSURL *requestURL = [endpoint urlForAuthServer:serverURL payload:jsonDictionary]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestURL]; + request.HTTPBody = [endpoint httpBodyForPayload:jsonDictionary error:&localError]; + if (localError) { + completionBlock(localError, nil); + return; + } + request.HTTPMethod = [endpoint httpMethod]; + request.timeoutInterval = MAX(timeout, 10); + NSDictionary *headers = [endpoint httpHeadersForPayload:jsonDictionary options:options]; + for (NSString *key in headers) { + [request addValue:headers[key] forHTTPHeaderField:key]; + } + RLMServerURLSessionCompletionBlock handler = ^(NSData *data, + NSURLResponse *response, + NSError *error) { + if (error != nil) { + // Network error + completionBlock(error, nil); + return; + } + + NSError *localError = nil; + + if (![self validateResponse:response data:data error:&localError]) { + // Response error + completionBlock(localError, nil); + return; + } + + // Parse out the JSON + id json = [NSJSONSerialization JSONObjectWithData:data + options:(NSJSONReadingOptions)0 + error:&localError]; + if (!json || localError) { + // JSON parsing error + completionBlock(localError, nil); + } else if (![json isKindOfClass:[NSDictionary class]]) { + // JSON response malformed + localError = make_auth_error_bad_response(json); + completionBlock(localError, nil); + } else { + // JSON parsed successfully + completionBlock(nil, (NSDictionary *)json); + } + }; + + // Add the request to a task and start it + NSURLSessionTask *task = [self.session dataTaskWithRequest:request + completionHandler:handler]; + [task resume]; +} + ++ (BOOL)validateResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError * __autoreleasing *)error { + __autoreleasing NSError *localError = nil; + if (!error) { + error = &localError; + } + + if (![response isKindOfClass:[NSHTTPURLResponse class]]) { + // FIXME: Provide error message + *error = make_auth_error_bad_response(); + return NO; + } + + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + BOOL badResponse = (NSLocationInRange(httpResponse.statusCode, rangeForErrorType(ClientError)) + || NSLocationInRange(httpResponse.statusCode, rangeForErrorType(ServerError))); + if (badResponse) { + if (RLMSyncErrorResponseModel *responseModel = [self responseModelFromData:data]) { + switch (responseModel.code) { + case RLMSyncAuthErrorInvalidParameters: + case RLMSyncAuthErrorMissingPath: + case RLMSyncAuthErrorInvalidCredential: + case RLMSyncAuthErrorUserDoesNotExist: + case RLMSyncAuthErrorUserAlreadyExists: + case RLMSyncAuthErrorAccessDeniedOrInvalidPath: + case RLMSyncAuthErrorInvalidAccessToken: + case RLMSyncAuthErrorExpiredPermissionOffer: + case RLMSyncAuthErrorAmbiguousPermissionOffer: + case RLMSyncAuthErrorFileCannotBeShared: + *error = make_auth_error(responseModel); + break; + default: + // Right now we assume that any codes not described + // above are generic HTTP error codes. + *error = make_auth_error_http_status(responseModel.status); + break; + } + } else { + *error = make_auth_error_http_status(httpResponse.statusCode); + } + + return NO; + } + + if (!data) { + // FIXME: provide error message + *error = make_auth_error_bad_response(); + return NO; + } + + return YES; +} + ++ (RLMSyncErrorResponseModel *)responseModelFromData:(NSData *)data { + if (data.length == 0) { + return nil; + } + id json = [NSJSONSerialization JSONObjectWithData:data + options:(NSJSONReadingOptions)0 + error:nil]; + if (!json || ![json isKindOfClass:[NSDictionary class]]) { + return nil; + } + return [[RLMSyncErrorResponseModel alloc] initWithDictionary:json]; +} + +@end + +@implementation RLMNetworkRequestOptions + +@end diff --git a/Pods/Realm/Realm/RLMObject.mm b/Pods/Realm/Realm/RLMObject.mm index c4878d7..1ace411 100644 --- a/Pods/Realm/Realm/RLMObject.mm +++ b/Pods/Realm/Realm/RLMObject.mm @@ -21,6 +21,7 @@ #import "RLMAccessor.h" #import "RLMArray.h" #import "RLMCollection_Private.hpp" +#import "RLMObjectBase_Private.h" #import "RLMObjectSchema_Private.hpp" #import "RLMObjectStore.h" #import "RLMProperty.h" @@ -65,9 +66,7 @@ - (instancetype)initWithRealm:(__unsafe_unretained RLMRealm *const)realm schema: #pragma mark - Convenience Initializers - (instancetype)initWithValue:(id)value { - [self.class sharedSchema]; // ensure this class' objectSchema is loaded in the partialSharedSchema - RLMSchema *schema = RLMSchema.partialSharedSchema; - return [super initWithValue:value schema:schema]; + return [super initWithValue:value schema:RLMSchema.partialPrivateSharedSchema]; } #pragma mark - Class-based Object Creation @@ -110,7 +109,7 @@ + (RLMResults *)allObjects { return RLMGetObjects(RLMRealm.defaultRealm, self.className, nil); } -+ (RLMResults *)allObjectsInRealm:(RLMRealm *)realm { ++ (RLMResults *)allObjectsInRealm:(__unsafe_unretained RLMRealm *const)realm { return RLMGetObjects(realm, self.className, nil); } @@ -265,6 +264,39 @@ - (id)copyWithZone:(__unused NSZone *)zone { @end +static bool treatFakeObjectAsRLMObject = false; +void RLMSetTreatFakeObjectAsRLMObject(BOOL flag) { + treatFakeObjectAsRLMObject = flag; +} + +BOOL RLMIsObjectOrSubclass(Class klass) { + if (RLMIsKindOfClass(klass, RLMObjectBase.class)) { + return YES; + } + + if (treatFakeObjectAsRLMObject) { + static Class FakeObjectClass = NSClassFromString(@"FakeObject"); + return RLMIsKindOfClass(klass, FakeObjectClass); + } + return NO; +} + +BOOL RLMIsObjectSubclass(Class klass) { + auto isSubclass = [](Class class1, Class class2) { + class1 = class_getSuperclass(class1); + return RLMIsKindOfClass(class1, class2); + }; + if (isSubclass(class_getSuperclass(klass), RLMObjectBase.class)) { + return YES; + } + + if (treatFakeObjectAsRLMObject) { + static Class FakeObjectClass = NSClassFromString(@"FakeObject"); + return isSubclass(klass, FakeObjectClass); + } + return NO; +} + @interface RLMObjectNotificationToken : RLMCancellationToken @end @implementation RLMObjectNotificationToken { @@ -277,7 +309,7 @@ @implementation RLMObjectNotificationToken { if (!obj->_realm) { @throw RLMException(@"Only objects which are managed by a Realm support change notifications"); } - [obj->_realm verifyNotificationsAreSupported]; + [obj->_realm verifyNotificationsAreSupported:true]; struct { void (^block)(NSArray *, NSArray *, NSArray *, NSError *); @@ -370,7 +402,7 @@ void error(std::exception_ptr err) { } callback{block, obj}; realm::Object object(obj->_realm->_realm, *obj->_info->objectSchema, obj->_row); - auto token = [[RLMObjectNotificationToken alloc] initWithToken:object.add_notification_block(callback) realm:obj->_realm]; + auto token = [[RLMObjectNotificationToken alloc] initWithToken:object.add_notification_callback(callback) realm:obj->_realm]; token->_object = std::move(object); return token; } diff --git a/Pods/Realm/Realm/RLMObjectBase.mm b/Pods/Realm/Realm/RLMObjectBase.mm index 8c87309..68d521f 100644 --- a/Pods/Realm/Realm/RLMObjectBase.mm +++ b/Pods/Realm/Realm/RLMObjectBase.mm @@ -33,6 +33,8 @@ #import "RLMUtil.hpp" #import "object.hpp" +#import "object_schema.hpp" +#import "shared_realm.hpp" using namespace realm; @@ -77,29 +79,30 @@ - (void)dealloc { _observationInfo = nullptr; } -static id validatedObjectForProperty(id obj, RLMProperty *prop, RLMSchema *schema) { - if (RLMIsObjectValidForProperty(obj, prop)) { - return obj; - } +static id coerceToObjectType(id obj, Class cls, RLMSchema *schema) { + return [obj isKindOfClass:cls] ? obj : [[cls alloc] initWithValue:obj schema:schema]; +} - // check for object or array of properties +static id validatedObjectForProperty(__unsafe_unretained id const obj, + __unsafe_unretained RLMObjectSchema *const objectSchema, + __unsafe_unretained RLMProperty *const prop, + __unsafe_unretained RLMSchema *const schema) { + RLMValidateValueForProperty(obj, objectSchema, prop); + if (!obj || obj == NSNull.null) { + return nil; + } if (prop.type == RLMPropertyTypeObject) { - // for object create and try to initialize with obj - RLMObjectSchema *objSchema = schema[prop.objectClassName]; - return [[objSchema.objectClass alloc] initWithValue:obj schema:schema]; - } - else if (prop.type == RLMPropertyTypeArray && [obj conformsToProtocol:@protocol(NSFastEnumeration)]) { - // for arrays, create objects for each element and return new array - RLMObjectSchema *objSchema = schema[prop.objectClassName]; - RLMArray *objects = [[RLMArray alloc] initWithObjectClassName:objSchema.className]; - for (id el in obj) { - [objects addObject:[[objSchema.objectClass alloc] initWithValue:el schema:schema]]; + Class objectClass = schema[prop.objectClassName].objectClass; + if (prop.array) { + NSMutableArray *ret = [[NSMutableArray alloc] init]; + for (id el in obj) { + [ret addObject:coerceToObjectType(el, objectClass, schema)]; + } + return ret; } - return objects; + return coerceToObjectType(obj, objectClass, schema); } - - // if not convertible to prop throw - @throw RLMException(@"Invalid value '%@' for property '%@'", obj, prop.name); + return obj; } - (instancetype)initWithValue:(id)value schema:(RLMSchema *)schema { @@ -107,6 +110,10 @@ - (instancetype)initWithValue:(id)value schema:(RLMSchema *)schema { return self; } + if (!value || value == NSNull.null) { + @throw RLMException(@"Must provide a non-nil value."); + } + if (!maybeInitObjectSchemaForUnmanaged(self)) { // Don't populate fields from the passed-in object if we're called // during schema init @@ -115,38 +122,30 @@ - (instancetype)initWithValue:(id)value schema:(RLMSchema *)schema { NSArray *properties = _objectSchema.properties; if (NSArray *array = RLMDynamicCast(value)) { - if (array.count != properties.count) { - @throw RLMException(@"Invalid array input. Number of array elements does not match number of properties."); + if (array.count > properties.count) { + @throw RLMException(@"Invalid array input: more values (%llu) than properties (%llu).", + (unsigned long long)array.count, (unsigned long long)properties.count); } - for (NSUInteger i = 0; i < array.count; i++) { - id propertyValue = validatedObjectForProperty(array[i], properties[i], schema); - [self setValue:RLMCoerceToNil(propertyValue) forKeyPath:[properties[i] name]]; + NSUInteger i = 0; + for (id val in array) { + RLMProperty *prop = properties[i++]; + [self setValue:validatedObjectForProperty(RLMCoerceToNil(val), _objectSchema, prop, schema) + forKey:prop.name]; } } - else if (value) { + else { // assume our object is an NSDictionary or an object with kvc properties - NSDictionary *defaultValues = nil; for (RLMProperty *prop in properties) { id obj = RLMValidatedValueForProperty(value, prop.name, _objectSchema.className); - // get default for nil object - if (!obj) { - if (!defaultValues) { - defaultValues = RLMDefaultValuesForObjectSchema(_objectSchema); - } - obj = defaultValues[prop.name]; - } - // don't set unspecified properties if (!obj) { continue; } - obj = validatedObjectForProperty(obj, prop, schema); - [self setValue:RLMCoerceToNil(obj) forKeyPath:prop.name]; + [self setValue:validatedObjectForProperty(RLMCoerceToNil(obj), _objectSchema, prop, schema) + forKey:prop.name]; } - } else { - @throw RLMException(@"Must provide a non-nil value."); } return self; @@ -184,16 +183,20 @@ - (id)valueForUndefinedKey:(NSString *)key { } - (void)setValue:(id)value forUndefinedKey:(NSString *)key { + value = RLMCoerceToNil(value); RLMProperty *property = _objectSchema[key]; if (Ivar ivar = property.swiftIvar) { - if (property.type == RLMPropertyTypeArray && [value conformsToProtocol:@protocol(NSFastEnumeration)]) { + if (property.array && (!value || [value conformsToProtocol:@protocol(NSFastEnumeration)])) { RLMArray *array = [object_getIvar(self, ivar) _rlmArray]; [array removeAllObjects]; - [array addObjects:validatedObjectForProperty(value, property, RLMSchema.partialSharedSchema)]; + + if (value) { + [array addObjects:validatedObjectForProperty(value, _objectSchema, property, + RLMSchema.partialPrivateSharedSchema)]; + } } else if (property.optional) { - RLMOptionalBase *optional = object_getIvar(self, ivar); - optional.underlyingValue = value; + RLMSetOptional(object_getIvar(self, ivar), value); } return; } @@ -214,6 +217,14 @@ + (RLMObjectSchema *)sharedSchema { return [RLMSchema sharedSchemaForClass:self.class]; } ++ (void)initializeLinkedObjectSchemas { + for (RLMProperty *prop in self.sharedSchema.properties) { + if (prop.type == RLMPropertyTypeObject && !RLMSchema.partialPrivateSharedSchema[prop.objectClassName]) { + [[RLMSchema classForString:prop.objectClassName] initializeLinkedObjectSchemas]; + } + } +} + + (Class)objectUtilClass:(BOOL)isSwift { return RLMObjectUtilClass(isSwift); } @@ -236,7 +247,7 @@ - (NSString *)descriptionWithMaxDepth:(NSUInteger)depth { NSMutableString *mString = [NSMutableString stringWithFormat:@"%@ {\n", baseClassName]; for (RLMProperty *property in _objectSchema.properties) { - id object = RLMObjectBaseObjectForKeyedSubscript(self, property.name); + id object = _realm ? RLMDynamicGetByName(self, property.name, true) : [self valueForKey:property.name]; NSString *sub; if ([object respondsToSelector:@selector(descriptionWithMaxDepth:)]) { sub = [object descriptionWithMaxDepth:depth - 1]; @@ -303,6 +314,10 @@ + (NSString *)_realmObjectName { return nil; } ++ (NSDictionary *)_realmColumnNames { + return nil; +} + - (id)mutableArrayValueForKey:(NSString *)key { id obj = [self valueForKey:key]; if ([obj isKindOfClass:[RLMArray class]]) { @@ -468,11 +483,7 @@ + (NSDictionary *)linkingObjectProperties:(__unused id)object { return nil; } -+ (NSArray *)getGenericListPropertyNames:(__unused id)obj { - return nil; -} - -+ (NSDictionary *)getLinkingObjectsProperties:(__unused id)obj { ++ (NSArray *)getSwiftProperties:(__unused id)obj { return nil; } @@ -485,3 +496,47 @@ + (NSArray *)requiredPropertiesForClass:(Class)cls { } @end + +@implementation RLMSwiftPropertyMetadata + ++ (instancetype)metadataForOtherProperty:(NSString *)propertyName { + RLMSwiftPropertyMetadata *md = [RLMSwiftPropertyMetadata new]; + md.propertyName = propertyName; + md.kind = RLMSwiftPropertyKindOther; + return md; +} + ++ (instancetype)metadataForListProperty:(NSString *)propertyName { + RLMSwiftPropertyMetadata *md = [RLMSwiftPropertyMetadata new]; + md.propertyName = propertyName; + md.kind = RLMSwiftPropertyKindList; + return md; +} + ++ (instancetype)metadataForLinkingObjectsProperty:(NSString *)propertyName + className:(NSString *)className + linkedPropertyName:(NSString *)linkedPropertyName { + RLMSwiftPropertyMetadata *md = [RLMSwiftPropertyMetadata new]; + md.propertyName = propertyName; + md.className = className; + md.linkedPropertyName = linkedPropertyName; + md.kind = RLMSwiftPropertyKindLinkingObjects; + return md; +} + ++ (instancetype)metadataForOptionalProperty:(NSString *)propertyName type:(RLMPropertyType)type { + RLMSwiftPropertyMetadata *md = [RLMSwiftPropertyMetadata new]; + md.propertyName = propertyName; + md.propertyType = type; + md.kind = RLMSwiftPropertyKindOptional; + return md; +} + ++ (instancetype)metadataForNilLiteralOptionalProperty:(NSString *)propertyName { + RLMSwiftPropertyMetadata *md = [RLMSwiftPropertyMetadata new]; + md.propertyName = propertyName; + md.kind = RLMSwiftPropertyKindNilLiteralOptional; + return md; +} + +@end diff --git a/Pods/Realm/Realm/RLMObjectSchema.mm b/Pods/Realm/Realm/RLMObjectSchema.mm index fb19575..0e5defb 100644 --- a/Pods/Realm/Realm/RLMObjectSchema.mm +++ b/Pods/Realm/Realm/RLMObjectSchema.mm @@ -28,6 +28,7 @@ #import "RLMSwiftSupport.h" #import "RLMUtil.hpp" +#import "object_schema.hpp" #import "object_store.hpp" using namespace realm; @@ -78,7 +79,9 @@ - (void)_propertiesDidChange { self.primaryKeyProperty = prop; } } + index = 0; for (RLMProperty *prop in _computedProperties) { + prop.index = index++; map[prop.name] = prop; } _allPropertiesByName = map; @@ -96,10 +99,14 @@ + (instancetype)schemaForObjectClass:(Class)objectClass { // determine classname from objectclass as className method has not yet been updated NSString *className = NSStringFromClass(objectClass); - bool isSwift = [RLMSwiftSupport isSwiftClassName:className]; - if (isSwift) { + bool hasSwiftName = [RLMSwiftSupport isSwiftClassName:className]; + if (hasSwiftName) { className = [RLMSwiftSupport demangleClassName:className]; } + + static Class s_swiftObjectClass = NSClassFromString(@"RealmSwiftObject"); + bool isSwift = hasSwiftName || [objectClass isSubclassOfClass:s_swiftObjectClass]; + schema.className = className; schema.objectClass = objectClass; schema.accessorClass = objectClass; @@ -110,7 +117,8 @@ + (instancetype)schemaForObjectClass:(Class)objectClass { Class superClass = class_getSuperclass(cls); NSArray *allProperties = @[]; while (superClass && superClass != RLMObjectBase.class) { - allProperties = [[RLMObjectSchema propertiesForClass:cls isSwift:isSwift] arrayByAddingObjectsFromArray:allProperties]; + allProperties = [[RLMObjectSchema propertiesForClass:cls isSwift:isSwift] + arrayByAddingObjectsFromArray:allProperties]; cls = superClass; superClass = class_getSuperclass(superClass); } @@ -127,14 +135,14 @@ + (instancetype)schemaForObjectClass:(Class)objectClass { // verify that we didn't add any properties twice due to inheritance if (allProperties.count != [NSSet setWithArray:[allProperties valueForKey:@"name"]].count) { NSCountedSet *countedPropertyNames = [NSCountedSet setWithArray:[allProperties valueForKey:@"name"]]; - NSSet *duplicatePropertyNames = [countedPropertyNames filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id object, NSDictionary *) { + NSArray *duplicatePropertyNames = [countedPropertyNames filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id object, NSDictionary *) { return [countedPropertyNames countForObject:object] > 1; - }]]; + }]].allObjects; if (duplicatePropertyNames.count == 1) { - @throw RLMException(@"Property '%@' is declared multiple times in the class hierarchy of '%@'", duplicatePropertyNames.allObjects.firstObject, className); + @throw RLMException(@"Property '%@' is declared multiple times in the class hierarchy of '%@'", duplicatePropertyNames.firstObject, className); } else { - @throw RLMException(@"Object '%@' has properties that are declared multiple times in its class hierarchy: '%@'", className, [duplicatePropertyNames.allObjects componentsJoinedByString:@"', '"]); + @throw RLMException(@"Object '%@' has properties that are declared multiple times in its class hierarchy: '%@'", className, [duplicatePropertyNames componentsJoinedByString:@"', '"]); } } @@ -151,41 +159,34 @@ + (instancetype)schemaForObjectClass:(Class)objectClass { @throw RLMException(@"Primary key property '%@' does not exist on object '%@'", primaryKey, className); } if (schema.primaryKeyProperty.type != RLMPropertyTypeInt && schema.primaryKeyProperty.type != RLMPropertyTypeString) { - @throw RLMException(@"Only 'string' and 'int' properties can be designated the primary key"); + @throw RLMException(@"Property '%@' cannot be made the primary key of '%@' because it is not a 'string' or 'int' property.", + primaryKey, className); } } for (RLMProperty *prop in schema.properties) { - if (prop.optional && !RLMPropertyTypeIsNullable(prop.type)) { - @throw RLMException(@"Only 'string', 'binary', and 'object' properties can be made optional, and property '%@' is of type '%@'.", - prop.name, RLMTypeToString(prop.type)); + if (prop.optional && prop.array && (prop.type == RLMPropertyTypeObject || prop.type == RLMPropertyTypeLinkingObjects)) { + // FIXME: message is awkward + @throw RLMException(@"Property '%@.%@' cannot be made optional because optional '%@' properties are not supported.", + className, prop.name, RLMTypeToString(prop.type)); } } return schema; } -+ (nullable NSString *)baseNameForLazySwiftProperty:(NSString *)propertyName { - // A Swift lazy var shows up as two separate children on the reflection tree: one named 'x', and another that is - // optional and is named 'x.storage'. Note that '.' is illegal in either a Swift or Objective-C property name. - NSString *const storageSuffix = @".storage"; - if ([propertyName hasSuffix:storageSuffix]) { - return [propertyName substringToIndex:propertyName.length - storageSuffix.length]; - } - return nil; -} - + (NSArray *)propertiesForClass:(Class)objectClass isSwift:(bool)isSwiftClass { Class objectUtil = [objectClass objectUtilClass:isSwiftClass]; NSArray *ignoredProperties = [objectUtil ignoredPropertiesForClass:objectClass]; NSDictionary *linkingObjectsProperties = [objectUtil linkingObjectsPropertiesForClass:objectClass]; + NSDictionary *columnNameMap = [objectClass _realmColumnNames]; // For Swift classes we need an instance of the object when parsing properties id swiftObjectInstance = isSwiftClass ? [[objectClass alloc] init] : nil; unsigned int count; - objc_property_t *props = class_copyPropertyList(objectClass, &count); - NSMutableArray *propArray = [NSMutableArray arrayWithCapacity:count]; + std::unique_ptr props(class_copyPropertyList(objectClass, &count), &free); + NSMutableArray *propArray = [NSMutableArray arrayWithCapacity:count]; NSSet *indexed = [[NSSet alloc] initWithArray:[objectUtil indexedPropertiesForClass:objectClass]]; for (unsigned int i = 0; i < count; i++) { NSString *propertyName = @(property_getName(props[i])); @@ -209,96 +210,22 @@ + (NSArray *)propertiesForClass:(Class)objectClass isSwift:(bool)isSwiftClass { } if (prop) { + if (columnNameMap) { + prop.columnName = columnNameMap[prop.name]; + } [propArray addObject:prop]; - } + } } - free(props); if (isSwiftClass) { - // List<> properties don't show up as objective-C properties due to - // being generic, so use Swift reflection to get a list of them, and - // then access their ivars directly - for (NSString *propName in [objectUtil getGenericListPropertyNames:swiftObjectInstance]) { - Ivar ivar = class_getInstanceVariable(objectClass, propName.UTF8String); - id value = object_getIvar(swiftObjectInstance, ivar); - NSString *className = [value _rlmArray].objectClassName; - NSUInteger existing = [propArray indexOfObjectPassingTest:^BOOL(RLMProperty *obj, __unused NSUInteger idx, __unused BOOL *stop) { - return [obj.name isEqualToString:propName]; - }]; - if (existing != NSNotFound) { - [propArray removeObjectAtIndex:existing]; - } - [propArray addObject:[[RLMProperty alloc] initSwiftListPropertyWithName:propName - ivar:ivar - objectClassName:className]]; - } - - // Ditto for LinkingObjects<> properties. - NSDictionary *linkingObjectsProperties = [objectUtil getLinkingObjectsProperties:swiftObjectInstance]; - for (NSString *propName in linkingObjectsProperties) { - NSDictionary *info = linkingObjectsProperties[propName]; - Ivar ivar = class_getInstanceVariable(objectClass, propName.UTF8String); - - NSUInteger existing = [propArray indexOfObjectPassingTest:^BOOL(RLMProperty *obj, __unused NSUInteger idx, __unused BOOL *stop) { - return [obj.name isEqualToString:propName]; - }]; - if (existing != NSNotFound) { - [propArray removeObjectAtIndex:existing]; - } - - [propArray addObject:[[RLMProperty alloc] initSwiftLinkingObjectsPropertyWithName:propName - ivar:ivar - objectClassName:info[@"class"] - linkOriginPropertyName:info[@"property"]]]; - } + [self addSwiftProperties:propArray objectUtil:objectUtil instance:swiftObjectInstance + indexed:indexed nameMap:columnNameMap]; } - if (auto optionalProperties = [objectUtil getOptionalProperties:swiftObjectInstance]) { - for (RLMProperty *property in propArray) { - property.optional = false; - } - [optionalProperties enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSNumber *propertyType, __unused BOOL *stop) { - if ([ignoredProperties containsObject:propertyName]) { - return; - } - NSUInteger existing = [propArray indexOfObjectPassingTest:^BOOL(RLMProperty *obj, __unused NSUInteger idx, __unused BOOL *stop) { - return [obj.name isEqualToString:propertyName]; - }]; - RLMProperty *property; - if (existing != NSNotFound) { - property = propArray[existing]; - property.optional = true; - } - if (auto type = RLMCoerceToNil(propertyType)) { - if (existing == NSNotFound) { - // Check to see if this optional property is an underlying storage property for a Swift lazy var. - // Managed lazy vars are't allowed. - // NOTE: Revisit this once property behaviors are implemented in Swift. - if (NSString *lazyPropertyBaseName = [self baseNameForLazySwiftProperty:propertyName]) { - if ([ignoredProperties containsObject:lazyPropertyBaseName]) { - // This property is the storage property for a ignored lazy Swift property. Just continue. - return; - } else { - @throw RLMException(@"Lazy managed property '%@' is not allowed on a Realm Swift object class. Either add the property to the ignored properties list or make it non-lazy.", lazyPropertyBaseName); - } - } - // The current property isn't a storage property for a lazy Swift property. - property = [[RLMProperty alloc] initSwiftOptionalPropertyWithName:propertyName - indexed:[indexed containsObject:propertyName] - ivar:class_getInstanceVariable(objectClass, propertyName.UTF8String) - propertyType:RLMPropertyType(type.intValue)]; - [propArray addObject:property]; - } - else { - property.type = RLMPropertyType(type.intValue); - } - } - }]; - } if (auto requiredProperties = [objectUtil requiredPropertiesForClass:objectClass]) { for (RLMProperty *property in propArray) { bool required = [requiredProperties containsObject:property.name]; - if (required && property.type == RLMPropertyTypeObject) { + if (required && property.type == RLMPropertyTypeObject && !property.array) { @throw RLMException(@"Object properties cannot be made required, " "but '+[%@ requiredProperties]' included '%@'", objectClass, property.name); } @@ -307,14 +234,107 @@ + (NSArray *)propertiesForClass:(Class)objectClass isSwift:(bool)isSwiftClass { } for (RLMProperty *property in propArray) { - if (!property.optional && property.type == RLMPropertyTypeObject) { // remove if/when core supports required link columns - @throw RLMException(@"The `%@.%@` property must be marked as being optional.", [objectClass className], property.name); + if (!property.optional && property.type == RLMPropertyTypeObject && !property.array) { + @throw RLMException(@"The `%@.%@` property must be marked as being optional.", + [objectClass className], property.name); } } return propArray; } ++ (void)addSwiftProperties:(NSMutableArray *)propArray + objectUtil:(Class)objectUtil + instance:(id)instance + indexed:(NSSet *)indexed + nameMap:(NSDictionary *)columnNameMap { + // The property list reported to the obj-c runtime for Swift objects is + // incomplete and doesn't include Swift generics like List<> and + // RealmOptional<>, and is missing information for some properties that + // are reported, such as the difference between `String` and `String?`. To + // deal with this, we also get the properties from Swift reflection, and + // merge the results. + + NSArray *props = [objectUtil getSwiftProperties:instance]; + if (!props) { + // A Swift subclass of RLMObject, which operates under obj-c rules + return; + } + + // Track the index that we expect the next property to go in, for inserting + // generic properties into the correct place + NSUInteger nextIndex = 0; + for (RLMSwiftPropertyMetadata *md in props) { + // In theory existing should only ever be nextIndex or NSNotFound, and + // this search is just a waste of time. + // FIXME: verify if this is actually true + NSUInteger existing = [propArray indexOfObjectPassingTest:^(RLMProperty *obj, NSUInteger, BOOL *) { + return [obj.name isEqualToString:md.propertyName]; + }]; + + RLMProperty *prop; + switch (md.kind) { + case RLMSwiftPropertyKindList: // List<> + prop = [[RLMProperty alloc] initSwiftListPropertyWithName:md.propertyName instance:instance]; + break; + case RLMSwiftPropertyKindLinkingObjects: { // LinkingObjects<> + Ivar ivar = class_getInstanceVariable([instance class], md.propertyName.UTF8String); + prop = [[RLMProperty alloc] initSwiftLinkingObjectsPropertyWithName:md.propertyName + ivar:ivar + objectClassName:md.className + linkOriginPropertyName:md.linkedPropertyName]; + break; + } + case RLMSwiftPropertyKindOptional: { + if (existing != NSNotFound) { + // String?, Data?, Date? with a non-nil default value + // We already know about this property from obj-c and we + // defaulted to optional, so nothing to do + break; + } + + // RealmOptional<> + Ivar ivar = class_getInstanceVariable([instance class], md.propertyName.UTF8String); + prop = [[RLMProperty alloc] initSwiftOptionalPropertyWithName:md.propertyName + indexed:[indexed containsObject:md.propertyName] + ivar:ivar + propertyType:md.propertyType]; + break; + } + + case RLMSwiftPropertyKindOther: + case RLMSwiftPropertyKindNilLiteralOptional: + // This might be a property which wasn't reported to obj-c and + // isn't one of our supported generic types, in which case we + // ignore it + if (existing == NSNotFound) { + --nextIndex; + } + // or it might be a String?, Data?, Date? or object field with + // a nil default value + else if (md.kind == RLMSwiftPropertyKindNilLiteralOptional) { + propArray[existing].optional = true; + } + // or it may be some non-optional property which may have been + // previously marked as optional due to that being the default + // in obj-c + else { + propArray[existing].optional = false; + } + break; + } + + if (prop) { + if (columnNameMap) { + prop.columnName = columnNameMap[prop.name]; + } + [propArray insertObject:prop atIndex:nextIndex]; + } + + ++nextIndex; + } +} + - (id)copyWithZone:(NSZone *)zone { RLMObjectSchema *schema = [[RLMObjectSchema allocWithZone:zone] init]; schema->_objectClass = _objectClass; @@ -361,17 +381,17 @@ - (NSString *)objectName { return [self.objectClass _realmObjectName] ?: _className; } -- (realm::ObjectSchema)objectStoreCopy { +- (realm::ObjectSchema)objectStoreCopy:(RLMSchema *)schema { ObjectSchema objectSchema; objectSchema.name = self.objectName.UTF8String; - objectSchema.primary_key = _primaryKeyProperty ? _primaryKeyProperty.name.UTF8String : ""; + objectSchema.primary_key = _primaryKeyProperty ? _primaryKeyProperty.columnName.UTF8String : ""; for (RLMProperty *prop in _properties) { - Property p = [prop objectStoreCopy]; + Property p = [prop objectStoreCopy:schema]; p.is_primary = (prop == _primaryKeyProperty); objectSchema.persisted_properties.push_back(std::move(p)); } for (RLMProperty *prop in _computedProperties) { - objectSchema.computed_properties.push_back([prop objectStoreCopy]); + objectSchema.computed_properties.push_back([prop objectStoreCopy:schema]); } return objectSchema; } @@ -408,7 +428,7 @@ + (instancetype)objectSchemaForObjectStoreSchema:(realm::ObjectSchema const&)obj schema.objectClass = RLMObject.class; schema.accessorClass = RLMDynamicObject.class; schema.unmanagedClass = RLMObject.class; - + return schema; } diff --git a/Pods/Realm/Realm/RLMObjectStore.mm b/Pods/Realm/Realm/RLMObjectStore.mm index 6878880..8b2ef34 100644 --- a/Pods/Realm/Realm/RLMObjectStore.mm +++ b/Pods/Realm/Realm/RLMObjectStore.mm @@ -18,7 +18,7 @@ #import "RLMObjectStore.h" -#import "RLMAccessor.h" +#import "RLMAccessor.hpp" #import "RLMArray_Private.hpp" #import "RLMListBase.h" #import "RLMObservation.hpp" @@ -40,6 +40,11 @@ using namespace realm; +@interface LinkingObjectsBase : NSObject +@property (nonatomic, nullable) RLMWeakObjectHandle *object; +@property (nonatomic, nullable) RLMProperty *property; +@end + void RLMRealmCreateAccessors(RLMSchema *schema) { const size_t bufferSize = sizeof("RLM:Managed ") // includes null terminator + std::numeric_limits::digits10 @@ -85,186 +90,26 @@ void RLMInitializeSwiftAccessorGenerics(__unsafe_unretained RLMObjectBase *const } for (RLMProperty *prop in object->_objectSchema.swiftGenericProperties) { - if (prop->_type == RLMPropertyTypeArray) { - RLMArray *array = [[RLMArrayLinkView alloc] initWithParent:object property:prop]; - [object_getIvar(object, prop.swiftIvar) set_rlmArray:array]; - } - else if (prop.type == RLMPropertyTypeLinkingObjects) { - id linkingObjects = object_getIvar(object, prop.swiftIvar); - [linkingObjects setObject:(id)[[RLMWeakObjectHandle alloc] initWithObject:object]]; - [linkingObjects setProperty:prop]; - } - else { - RLMOptionalBase *optional = object_getIvar(object, prop.swiftIvar); - optional.property = prop; - optional.object = object; + id ivar = object_getIvar(object, prop.swiftIvar); + if (!ivar) { + // FIXME: this should actually be an error as it's the result of an + // invalid object definition, but that's a breaking change so + // instead preserve the old behavior until the next major version bump + // https://github.com/realm/realm-cocoa/issues/5784 + continue; } - } -} -static void validateValueForProperty(__unsafe_unretained id const obj, - __unsafe_unretained RLMProperty *const prop) { - switch (prop.type) { - case RLMPropertyTypeString: - case RLMPropertyTypeBool: - case RLMPropertyTypeDate: - case RLMPropertyTypeInt: - case RLMPropertyTypeFloat: - case RLMPropertyTypeDouble: - case RLMPropertyTypeData: - if (!RLMIsObjectValidForProperty(obj, prop)) { - @throw RLMException(@"Invalid value '%@' for property '%@'", obj, prop.name); - } - break; - case RLMPropertyTypeObject: - break; - case RLMPropertyTypeArray: { - if (obj != nil && obj != NSNull.null) { - if (![obj conformsToProtocol:@protocol(NSFastEnumeration)]) { - @throw RLMException(@"Array property value (%@) is not enumerable.", obj); - } - } - break; + if (prop.type == RLMPropertyTypeLinkingObjects) { + [ivar setObject:(id)[[RLMWeakObjectHandle alloc] initWithObject:object]]; + [ivar setProperty:prop]; } - case RLMPropertyTypeAny: - case RLMPropertyTypeLinkingObjects: - @throw RLMException(@"Invalid value '%@' for property '%@'", obj, prop.name); - } -} - -static NSUInteger createRowForObject(RLMClassInfo const& info) { - try { - return info.table()->add_empty_row(); - } - catch (std::exception const& e) { - @throw RLMException(e); - } -} - -/* If a row exists with the specified primary key value, return its index. Otherwise, return `realm::not_found`. - * - * Precondition: `info` must refer to a class which has a primary key property - * Precondition: `primaryValue` is a validated property value that has been coerced to `nil` - */ -static NSUInteger getRowForObjectWithPrimaryKey(RLMClassInfo const& info, id primaryValue) { - REALM_ASSERT_DEBUG(info.propertyForPrimaryKey()); - - RLMProperty *const primaryProperty = info.propertyForPrimaryKey(); - const NSUInteger primaryPropertyColumn = info.tableColumn(primaryProperty); - - switch (primaryProperty.type) { - case RLMPropertyTypeString: - return info.table()->find_first_string(primaryPropertyColumn, RLMStringDataWithNSString(primaryValue)); - - case RLMPropertyTypeInt: - if (primaryValue) { - return info.table()->find_first_int(primaryPropertyColumn, [primaryValue longLongValue]); - } else { - return info.table()->find_first_null(primaryPropertyColumn); - } - - default: - REALM_UNREACHABLE(); - } -} - -/* Create a row with the specified primary key value and return its index. - * - * Precondition: `info` must refer to a class which has a valid primary key property - * Precondition: a write transaction is in progress - * Precondition: no row already exists with the specified `primaryValue` for this model - */ -static NSUInteger createRowForObjectWithPrimaryKey(RLMClassInfo const& info, id primaryValue) { - REALM_ASSERT_DEBUG(info.propertyForPrimaryKey()); - REALM_ASSERT_DEBUG(info.realm.inWriteTransaction); - REALM_ASSERT_DEBUG(getRowForObjectWithPrimaryKey(info, primaryValue) == realm::not_found); - - RLMProperty *const primaryProperty = info.propertyForPrimaryKey(); - const NSUInteger primaryColumnIndex = info.tableColumn(primaryProperty); - - // create row - const NSUInteger rowIndex = createRowForObject(info); - Row row = info.table()->get(rowIndex); - - // set value for primary key - validateValueForProperty(primaryValue, primaryProperty); - primaryValue = RLMCoerceToNil(primaryValue); - - try { - switch (primaryProperty.type) { - case RLMPropertyTypeString: - REALM_ASSERT_DEBUG(!primaryValue || [primaryValue isKindOfClass:NSString.class]); - row.set_string_unique(primaryColumnIndex, RLMStringDataWithNSString(primaryValue)); - break; - - case RLMPropertyTypeInt: - if (primaryValue) { - REALM_ASSERT_DEBUG([primaryValue isKindOfClass:NSNumber.class]); - row.set_int_unique(primaryColumnIndex, [primaryValue longLongValue]); - } else { - row.set_null(primaryColumnIndex); // FIXME: Use `set_null_unique` once Core supports it - } - break; - - default: - REALM_UNREACHABLE(); + else if (prop.array) { + RLMArray *array = [[RLMManagedArray alloc] initWithParent:object property:prop]; + [ivar set_rlmArray:array]; } - } - catch (std::exception const& e) { - @throw RLMException(e); - } - return rowIndex; -} - -/* If a row exists with the specified primary key value, returns its index. Otherwise, creates a new row with the - * specified primary key value and returns its index. The out parameter `foundExisting` will be set to indicate - * whether or not a new row was created. - * - * Precondition: `info` must refer to a class which has a valid primary key property - * Precondition: a write transaction is in progress - */ -static NSUInteger createOrGetRowForObjectWithPrimaryKey(RLMClassInfo const& info, id primaryValue, - bool* foundExisting = nullptr) { - REALM_ASSERT_DEBUG(info.propertyForPrimaryKey()); - REALM_ASSERT_DEBUG(info.realm.inWriteTransaction); - - const NSUInteger existingRow = getRowForObjectWithPrimaryKey(info, primaryValue); - if (existingRow == realm::not_found) { - *foundExisting = false; - return createRowForObjectWithPrimaryKey(info, primaryValue); - } else { - *foundExisting = true; - return existingRow; - } -} - -/* If the class has a primary key, calls `valueForProperty` with that key and creates or gets the row with - * this primary key value. Otherwise if the class has no primary key, creates a new row. The out parameter - * `foundExisting` will be set to indicate whether or not a new row was created. - * - * Precondition: a write transaction is in progress - */ -template -static NSUInteger createOrGetRowForObject(RLMClassInfo const& info, F valueForProperty, - bool createOrUpdate, bool* foundExisting) { - // try to get existing row if this class has a primary key - if (RLMProperty *primaryProperty = info.propertyForPrimaryKey()) { - // get primary value - const id primaryValue = valueForProperty(primaryProperty); - - // search for existing object based on primary key type, creating a new row if one does not exist - NSUInteger rowIndex = createOrGetRowForObjectWithPrimaryKey(info, RLMCoerceToNil(primaryValue), foundExisting); - - // ensure that `createOrUpdate` is set if we found an existing row - if (*foundExisting && !createOrUpdate) { - @throw RLMException(@"Can't create object with existing primary key value '%@'.", primaryValue); + else { + RLMInitializeManagedOptional(ivar, object, prop); } - return rowIndex; - } - // if no existing, create row - else { - *foundExisting = false; - return createRowForObject(info); } } @@ -279,162 +124,64 @@ void RLMAddObjectToRealm(__unsafe_unretained RLMObjectBase *const object, } if (object->_realm) { if (object->_realm == realm) { - // no-op + // Adding an object to the Realm it's already manged by is a no-op return; } // for differing realms users must explicitly create the object in the second realm - @throw RLMException(@"Object is already managed by another Realm"); + @throw RLMException(@"Object is already managed by another Realm. Use create instead to copy it into this Realm."); } if (object->_observationInfo && object->_observationInfo->hasObservers()) { @throw RLMException(@"Cannot add an object with observers to a Realm"); } - // set the realm and schema - NSString *objectClassName = object->_objectSchema.className; - auto& info = realm->_info[objectClassName]; + auto& info = realm->_info[object->_objectSchema.className]; + RLMAccessorContext c{realm, info, true}; object->_info = &info; - object->_objectSchema = info.rlmObjectSchema; object->_realm = realm; - - // get or create row - bool foundExisting; - auto primaryGetter = [=](__unsafe_unretained RLMProperty *const p) { return [object valueForKey:p.name]; }; - object->_row = (*info.table())[createOrGetRowForObject(info, primaryGetter, createOrUpdate, &foundExisting)]; - - RLMCreationOptions creationOptions = RLMCreationOptionsPromoteUnmanaged; - if (createOrUpdate) { - creationOptions |= RLMCreationOptionsCreateOrUpdate; + object->_objectSchema = info.rlmObjectSchema; + try { + realm::Object::create(c, realm->_realm, *info.objectSchema, (id)object, + createOrUpdate, &object->_row); } - - // populate all properties - for (RLMProperty *prop in info.rlmObjectSchema.properties) { - // skip primary key when updating since it doesn't change - if (prop.isPrimary) - continue; - - // get object from ivar using key value coding - id value = nil; - if (prop.swiftIvar) { - if (prop.type == RLMPropertyTypeArray) { - value = static_cast(object_getIvar(object, prop.swiftIvar))._rlmArray; - } - else { // optional - value = static_cast(object_getIvar(object, prop.swiftIvar)).underlyingValue; - } - } - else if ([object respondsToSelector:prop.getterSel]) { - value = [object valueForKey:prop.getterName]; - } - - if (!value && !prop.optional) { - @throw RLMException(@"No value or default value specified for property '%@' in '%@'", - prop.name, info.rlmObjectSchema.className); - } - - // set the ivars for object and array properties to nil as otherwise the - // accessors retain objects that are no longer accessible via the properties - // this is mainly an issue when the object graph being added has cycles, - // as it's not obvious that the user has to set the *ivars* to nil to - // avoid leaking memory - if (prop.type == RLMPropertyTypeObject || prop.type == RLMPropertyTypeArray) { - if (!prop.swiftIvar) { - ((void(*)(id, SEL, id))objc_msgSend)(object, prop.setterSel, nil); - } - } - - // set in table with out validation - RLMDynamicSet(object, prop, RLMCoerceToNil(value), creationOptions); + catch (std::exception const& e) { + @throw RLMException(e); } - - // set to proper accessor class object_setClass(object, info.rlmObjectSchema.accessorClass); - RLMInitializeSwiftAccessorGenerics(object); } -RLMObjectBase *RLMCreateObjectInRealmWithValue(RLMRealm *realm, NSString *className, id value, bool createOrUpdate = false) { +RLMObjectBase *RLMCreateObjectInRealmWithValue(RLMRealm *realm, NSString *className, + id value, bool createOrUpdate = false) { + RLMVerifyInWriteTransaction(realm); + if (createOrUpdate && RLMIsObjectSubclass([value class])) { RLMObjectBase *obj = value; - if ([obj->_objectSchema.className isEqualToString:className] && obj->_realm == realm) { + if (obj->_realm == realm && [obj->_objectSchema.className isEqualToString:className]) { // This is a no-op if value is an RLMObject of the same type already backed by the target realm. return value; } } - // verify writable - RLMVerifyInWriteTransaction(realm); + if (!value || value == NSNull.null) { + @throw RLMException(@"Must provide a non-nil value."); + } - // create the object auto& info = realm->_info[className]; - RLMObjectBase *object = RLMCreateManagedAccessor(info.rlmObjectSchema.accessorClass, realm, &info); + if ([value isKindOfClass:[NSArray class]] && [value count] > info.objectSchema->persisted_properties.size()) { + @throw RLMException(@"Invalid array input: more values (%llu) than properties (%llu).", + (unsigned long long)[value count], + (unsigned long long)info.objectSchema->persisted_properties.size()); + } - RLMCreationOptions creationOptions = createOrUpdate ? RLMCreationOptionsCreateOrUpdate : RLMCreationOptionsNone; - - // create row, and populate - if (NSArray *array = RLMDynamicCast(value)) { - // get or create our accessor - bool foundExisting; - NSArray *props = info.rlmObjectSchema.properties; - auto primaryGetter = [=](__unsafe_unretained RLMProperty *const p) { - return array[[props indexOfObject:p]]; - }; - object->_row = (*info.table())[createOrGetRowForObject(info, primaryGetter, createOrUpdate, &foundExisting)]; - - // populate - for (NSUInteger i = 0; i < array.count; i++) { - RLMProperty *prop = props[i]; - - // skip primary key when updating since it doesn't change - if (prop.isPrimary) - continue; - - id val = array[i]; - validateValueForProperty(val, prop); - RLMDynamicSet(object, prop, RLMCoerceToNil(val), creationOptions); - } + RLMAccessorContext c{realm, info, false}; + RLMObjectBase *object = RLMCreateManagedAccessor(info.rlmObjectSchema.accessorClass, realm, &info); + try { + object->_row = realm::Object::create(c, realm->_realm, *info.objectSchema, + (id)value, createOrUpdate).row(); } - else { - __block bool foundExisting = false; - __block NSDictionary *defaultValues = nil; - __block bool usedDefault = false; - auto getValue = ^(RLMProperty *prop) { - id propValue = RLMValidatedValueForProperty(value, prop.name, info.rlmObjectSchema.className); - usedDefault = !propValue && !foundExisting; - if (usedDefault) { - if (!defaultValues) { - defaultValues = RLMDefaultValuesForObjectSchema(info.rlmObjectSchema); - } - propValue = defaultValues[prop.name]; - if (!propValue && (prop.type == RLMPropertyTypeObject || prop.type == RLMPropertyTypeArray)) { - propValue = NSNull.null; - } - } - return propValue; - }; - // get or create our accessor - object->_row = (*info.table())[createOrGetRowForObject(info, getValue, createOrUpdate, &foundExisting)]; - - // populate - for (RLMProperty *prop in info.rlmObjectSchema.properties) { - // skip primary key when updating since it doesn't change - if (prop.isPrimary) - continue; - - if (id propValue = getValue(prop)) { - validateValueForProperty(propValue, prop); - // add SetDefault to creationoptions - RLMCreationOptions propertyCreationOptions = creationOptions; - if (usedDefault) { - propertyCreationOptions |= RLMCreationOptionsSetDefault; - } - RLMDynamicSet(object, prop, RLMCoerceToNil(propValue), propertyCreationOptions); - } - else if (!foundExisting && !prop.optional) { - @throw RLMException(@"Property '%@' of object of type '%@' cannot be nil.", prop.name, info.rlmObjectSchema.className); - } - } + catch (std::exception const& e) { + @throw RLMException(e); } - RLMInitializeSwiftAccessorGenerics(object); return object; } @@ -450,7 +197,7 @@ void RLMDeleteObjectFromRealm(__unsafe_unretained RLMObjectBase *const object, // move last row to row we are deleting if (object->_row.is_attached()) { RLMTrackDeletions(realm, ^{ - object->_row.get_table()->move_last_over(object->_row.get_index()); + object->_row.move_last_over(); }); } @@ -467,7 +214,9 @@ void RLMDeleteAllObjectsFromRealm(RLMRealm *realm) { } } -RLMResults *RLMGetObjects(RLMRealm *realm, NSString *objectClassName, NSPredicate *predicate) { +RLMResults *RLMGetObjects(__unsafe_unretained RLMRealm *const realm, + NSString *objectClassName, + NSPredicate *predicate) { RLMVerifyRealmRead(realm); // create view from table and predicate @@ -491,53 +240,21 @@ void RLMDeleteAllObjectsFromRealm(RLMRealm *realm) { id RLMGetObject(RLMRealm *realm, NSString *objectClassName, id key) { RLMVerifyRealmRead(realm); - RLMClassInfo& info = realm->_info[objectClassName]; - auto primaryProperty = info.objectSchema->primary_key_property(); - if (!primaryProperty) { - @throw RLMException(@"%@ does not have a primary key", objectClassName); - } - - auto table = info.table(); - if (!table) { - // read-only realms may be missing tables since we can't add any - // missing ones on init - return nil; - } - - key = RLMCoerceToNil(key); - if (!key && !primaryProperty->is_nullable) { - @throw RLMException(@"Invalid null value for non-nullable primary key."); + auto& info = realm->_info[objectClassName]; + if (RLMProperty *prop = info.propertyForPrimaryKey()) { + RLMValidateValueForProperty(key, info.rlmObjectSchema, prop); } - - size_t row = realm::not_found; - switch (primaryProperty->type) { - case PropertyType::String: { - NSString *string = RLMDynamicCast(key); - if (!key || string) { - row = table->find_first_string(primaryProperty->table_column, RLMStringDataWithNSString(string)); - } else { - @throw RLMException(@"Invalid value '%@' of type '%@' for string primary key.", key, [key class]); - } - break; - } - case PropertyType::Int: - if (NSNumber *number = RLMDynamicCast(key)) { - row = table->find_first_int(primaryProperty->table_column, number.longLongValue); - } else if (!key) { - row = table->find_first_null(primaryProperty->table_column); - } else { - @throw RLMException(@"Invalid value '%@' of type '%@' for int primary key.", key, [key class]); - } - break; - default: - REALM_UNREACHABLE(); + try { + RLMAccessorContext c{realm, info}; + auto obj = realm::Object::get_for_primary_key(c, realm->_realm, *info.objectSchema, + key ?: NSNull.null); + if (!obj.is_valid()) + return nil; + return RLMCreateObjectAccessor(realm, info, obj.row()); } - - if (row == realm::not_found) { - return nil; + catch (std::exception const& e) { + @throw RLMException(e); } - - return RLMCreateObjectAccessor(realm, info, row); } RLMObjectBase *RLMCreateObjectAccessor(__unsafe_unretained RLMRealm *const realm, diff --git a/Pods/Realm/Realm/RLMObservation.mm b/Pods/Realm/Realm/RLMObservation.mm index e6538b6..f8102ed 100644 --- a/Pods/Realm/Realm/RLMObservation.mm +++ b/Pods/Realm/Realm/RLMObservation.mm @@ -181,7 +181,7 @@ auto reverse(Container const& c) { NSUInteger sep = [keyPath rangeOfString:@"."].location; NSString *key = sep == NSNotFound ? keyPath : [keyPath substringToIndex:sep]; RLMProperty *prop = objectSchema[key]; - if (prop && prop.type == RLMPropertyTypeArray) { + if (prop && prop.array) { id value = valueForKey(key); RLMArray *array = [value isKindOfClass:[RLMListBase class]] ? [value _rlmArray] : value; array->_key = key; @@ -189,8 +189,7 @@ auto reverse(Container const& c) { } else if (auto swiftIvar = prop.swiftIvar) { if (auto optional = RLMDynamicCast(object_getIvar(object, swiftIvar))) { - optional.property = prop; - optional.object = object; + RLMInitializeUnmanagedOptional(optional, object, prop); } } } @@ -225,7 +224,7 @@ auto reverse(Container const& c) { // We need to return the same object each time for observing over keypaths // to work, so we store a cache of them here. We can't just cache them on // the object as that leads to retain cycles. - if (lastProp.type == RLMPropertyTypeArray) { + if (lastProp.array) { RLMArray *value = cachedObjects[key]; if (!value) { value = getSuper(); @@ -280,7 +279,7 @@ void RLMClearTable(RLMClassInfo &objectSchema) { } RLMTrackDeletions(objectSchema.realm, ^{ - objectSchema.table()->clear(); + Results(objectSchema.realm->_realm, *objectSchema.table()).clear(); for (auto info : objectSchema.observedObjects) { info->prepareForInvalidation(); diff --git a/Pods/Realm/Realm/RLMOptionalBase.mm b/Pods/Realm/Realm/RLMOptionalBase.mm index 8aad1c8..bbe4c09 100644 --- a/Pods/Realm/Realm/RLMOptionalBase.mm +++ b/Pods/Realm/Realm/RLMOptionalBase.mm @@ -16,74 +16,158 @@ // //////////////////////////////////////////////////////////////////////////// -#import "RLMAccessor.h" #import "RLMOptionalBase.h" -#import "RLMObject_Private.h" -#import "RLMObjectStore.h" -#import "RLMProperty_Private.h" -#import "RLMUtil.hpp" -#import +#import "RLMAccessor.hpp" +#import "RLMObject_Private.hpp" +#import "RLMProperty.h" +#import "RLMUtil.hpp" +#import "object.hpp" -@interface RLMOptionalBase () -@property (nonatomic) id unmanagedValue; -@end +namespace { +struct OptionalBase { + virtual id get() = 0; + virtual void set(id) = 0; + virtual ~OptionalBase() = default; +}; -@implementation RLMOptionalBase +class UnmanagedOptional : public OptionalBase { +public: + id get() override { + return _value; + } -- (instancetype)init { - return self; -} + void set(__unsafe_unretained const id newValue) override { + @autoreleasepool { + RLMObjectBase *object = _parent; + [object willChangeValueForKey:_property]; + _value = newValue; + [object didChangeValueForKey:_property]; + } + } -- (id)underlyingValue { - if ((_object && _object->_realm) || _object.isInvalidated) { - return RLMDynamicGet(_object, _property); + void attach(__unsafe_unretained RLMObjectBase *const obj, NSString *property) { + if (!_property) { + _property = property; + _parent = obj; + } } - else { - return _unmanagedValue; + +private: + id _value; + NSString *_property; + __weak RLMObjectBase *_parent; + +}; + +class ManagedOptional : public OptionalBase { +public: + ManagedOptional(RLMObjectBase *obj, RLMProperty *prop) + : _realm(obj->_realm) + , _object(obj->_realm->_realm, *obj->_info->objectSchema, obj->_row) + , _propertyName(prop.name.UTF8String) + , _ctx(obj->_realm, *obj->_info) + { } -} -- (void)setUnderlyingValue:(id)underlyingValue { - if ((_object && _object->_realm) || _object.isInvalidated) { - if (_property.isPrimary) { - @throw RLMException(@"Primary key can't be changed after an object is inserted."); - } - RLMDynamicSet(_object, _property, underlyingValue, RLMCreationOptionsNone); + id get() override { + return _object.get_property_value(_ctx, _propertyName); } - else { - NSString *propertyName = _property.name; - [_object willChangeValueForKey:propertyName]; - _unmanagedValue = underlyingValue; - [_object didChangeValueForKey:propertyName]; + + void set(__unsafe_unretained id const value) override { + _object.set_property_value(_ctx, _propertyName, value ?: NSNull.null, false); } + +private: + // We have to hold onto a strong reference to the Realm as + // RLMAccessorContext holds a non-retaining one. + __unused RLMRealm *_realm; + realm::Object _object; + std::string _propertyName; + RLMAccessorContext _ctx; +}; +} // anonymous namespace + +@interface RLMOptionalBase () { + std::unique_ptr _impl; +} +@end + +@implementation RLMOptionalBase +- (instancetype)init { + return self; } - (BOOL)isKindOfClass:(Class)aClass { - return [self.underlyingValue isKindOfClass:aClass] || RLMIsKindOfClass(object_getClass(self), aClass); + return [RLMGetOptional(self) isKindOfClass:aClass] || RLMIsKindOfClass(object_getClass(self), aClass); } - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { - return [self.underlyingValue methodSignatureForSelector:sel]; + return [RLMGetOptional(self) methodSignatureForSelector:sel]; } - (void)forwardInvocation:(NSInvocation *)invocation { - [invocation invokeWithTarget:self.underlyingValue]; + [invocation invokeWithTarget:RLMGetOptional(self)]; } - (id)forwardingTargetForSelector:(__unused SEL)sel { - return self.underlyingValue; + return RLMGetOptional(self); } - (BOOL)respondsToSelector:(SEL)aSelector { - if (id val = self.underlyingValue) { - return [val respondsToSelector:aSelector]; - } - return NO; + return [RLMGetOptional(self) respondsToSelector:aSelector]; } - (void)doesNotRecognizeSelector:(SEL)aSelector { - [self.underlyingValue doesNotRecognizeSelector:aSelector]; + [RLMGetOptional(self) doesNotRecognizeSelector:aSelector]; } +id RLMGetOptional(__unsafe_unretained RLMOptionalBase *const self) { + if (!self) { + // FIXME: this should actually be an error + // https://github.com/realm/realm-cocoa/issues/5784 + return nil; + } + try { + return self->_impl ? RLMCoerceToNil(self->_impl->get()) : nil; + } + catch (std::exception const& err) { + @throw RLMException(err); + } +} + +void RLMSetOptional(__unsafe_unretained RLMOptionalBase *const self, __unsafe_unretained const id value) { + if (!self) { + // FIXME: this should actually be an error + // https://github.com/realm/realm-cocoa/issues/5784 + return; + } + try { + if (!self->_impl && value) { + self->_impl.reset(new UnmanagedOptional); + } + if (self->_impl) { + self->_impl->set(value); + } + } + catch (std::exception const& err) { + @throw RLMException(err); + } +} + +void RLMInitializeManagedOptional(__unsafe_unretained RLMOptionalBase *const self, + __unsafe_unretained RLMObjectBase *const parent, + __unsafe_unretained RLMProperty *const prop) { + REALM_ASSERT(parent->_realm); + self->_impl.reset(new ManagedOptional(parent, prop)); +} + +void RLMInitializeUnmanagedOptional(__unsafe_unretained RLMOptionalBase *const self, + __unsafe_unretained RLMObjectBase *const parent, + __unsafe_unretained RLMProperty *const prop) { + if (!self->_impl) { + self->_impl.reset(new UnmanagedOptional); + } + static_cast(*self->_impl).attach(parent, prop.name); +} @end diff --git a/Pods/Realm/Realm/RLMPredicateUtil.mm b/Pods/Realm/Realm/RLMPredicateUtil.mm index 247850b..830a9b2 100644 --- a/Pods/Realm/Realm/RLMPredicateUtil.mm +++ b/Pods/Realm/Realm/RLMPredicateUtil.mm @@ -90,7 +90,10 @@ - (NSExpression *)falseExpression; } case NSConditionalExpressionType: +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpartial-availability" return [NSExpression expressionForConditional:visit(expression.predicate) trueExpression:visit(expression.trueExpression) falseExpression:visit(expression.falseExpression)]; +#pragma clang diagnostic pop default: // The remaining expression types do not contain nested expressions or predicates. diff --git a/Pods/Realm/Realm/RLMProperty.mm b/Pods/Realm/Realm/RLMProperty.mm index 9c5c79e..490fadc 100644 --- a/Pods/Realm/Realm/RLMProperty.mm +++ b/Pods/Realm/Realm/RLMProperty.mm @@ -18,18 +18,25 @@ #import "RLMProperty_Private.hpp" -#import "RLMArray.h" +#import "RLMArray_Private.hpp" #import "RLMListBase.h" #import "RLMObject.h" +#import "RLMObjectSchema_Private.hpp" #import "RLMObject_Private.h" -#import "RLMOptionalBase.h" #import "RLMSchema_Private.h" #import "RLMSwiftSupport.h" #import "RLMUtil.hpp" -BOOL RLMPropertyTypeIsNullable(RLMPropertyType propertyType) { - return propertyType != RLMPropertyTypeArray && propertyType != RLMPropertyTypeLinkingObjects; -} +#import "property.hpp" + +static_assert((int)RLMPropertyTypeInt == (int)realm::PropertyType::Int, ""); +static_assert((int)RLMPropertyTypeBool == (int)realm::PropertyType::Bool, ""); +static_assert((int)RLMPropertyTypeFloat == (int)realm::PropertyType::Float, ""); +static_assert((int)RLMPropertyTypeDouble == (int)realm::PropertyType::Double, ""); +static_assert((int)RLMPropertyTypeString == (int)realm::PropertyType::String, ""); +static_assert((int)RLMPropertyTypeData == (int)realm::PropertyType::Data, ""); +static_assert((int)RLMPropertyTypeDate == (int)realm::PropertyType::Date, ""); +static_assert((int)RLMPropertyTypeObject == (int)realm::PropertyType::Object, ""); BOOL RLMPropertyTypeIsComputed(RLMPropertyType propertyType) { return propertyType == RLMPropertyTypeLinkingObjects; @@ -69,19 +76,23 @@ void RLMValidateSwiftPropertyName(NSString *name) { } } -static bool rawTypeIsComputedProperty(NSString *rawType) { +static bool rawTypeShouldBeTreatedAsComputedProperty(NSString *rawType) { return [rawType isEqualToString:@"@\"RLMLinkingObjects\""] || [rawType hasPrefix:@"@\"RLMLinkingObjects<"]; } @implementation RLMProperty + (instancetype)propertyForObjectStoreProperty:(const realm::Property &)prop { - return [[RLMProperty alloc] initWithName:@(prop.name.c_str()) - type:(RLMPropertyType)prop.type - objectClassName:prop.object_type.length() ? @(prop.object_type.c_str()) : nil - linkOriginPropertyName:prop.link_origin_property_name.length() ? @(prop.link_origin_property_name.c_str()) : nil - indexed:prop.is_indexed - optional:prop.is_nullable]; + auto ret = [[RLMProperty alloc] initWithName:@(prop.name.c_str()) + type:static_cast(prop.type & ~realm::PropertyType::Flags) + objectClassName:prop.object_type.length() ? @(prop.object_type.c_str()) : nil + linkOriginPropertyName:prop.link_origin_property_name.length() ? @(prop.link_origin_property_name.c_str()) : nil + indexed:prop.is_indexed + optional:is_nullable(prop.type)]; + if (is_array(prop.type)) { + ret->_array = true; + } + return ret; } - (instancetype)initWithName:(NSString *)name @@ -127,6 +138,35 @@ - (void)updateAccessors { _setterSel = NSSelectorFromString(_setterName); } +static realm::util::Optional typeFromProtocolString(const char *type) { + if (strncmp(type, "RLM", 3)) { + return realm::none; + } + type += 3; + if (strcmp(type, "Int>\"") == 0) { + return RLMPropertyTypeInt; + } + if (strcmp(type, "Float>\"") == 0) { + return RLMPropertyTypeFloat; + } + if (strcmp(type, "Double>\"") == 0) { + return RLMPropertyTypeDouble; + } + if (strcmp(type, "Bool>\"") == 0) { + return RLMPropertyTypeBool; + } + if (strcmp(type, "String>\"") == 0) { + return RLMPropertyTypeString; + } + if (strcmp(type, "Data>\"") == 0) { + return RLMPropertyTypeData; + } + if (strcmp(type, "Date>\"") == 0) { + return RLMPropertyTypeDate; + } + return realm::none; +} + // determine RLMPropertyType from objc code - returns true if valid type was found/set - (BOOL)setTypeFromRawType:(NSString *)rawType { const char *code = rawType.UTF8String; @@ -173,48 +213,41 @@ - (BOOL)setTypeFromRawType:(NSString *)rawType { _type = RLMPropertyTypeData; } else if (strncmp(code, arrayPrefix, arrayPrefixLen) == 0) { - _optional = false; + _array = true; + if (auto type = typeFromProtocolString(code + arrayPrefixLen)) { + _type = *type; + return YES; + } + // get object class from type string - @"RLMArray" - _type = RLMPropertyTypeArray; _objectClassName = [[NSString alloc] initWithBytes:code + arrayPrefixLen length:strlen(code + arrayPrefixLen) - 2 // drop trailing >" encoding:NSUTF8StringEncoding]; - Class cls = [RLMSchema classForString:_objectClassName]; - if (!cls) { - @throw RLMException(@"Property '%@' is of type 'RLMArray<%@>' which is not a supported RLMArray object type. " - @"RLMArrays can only contain instances of RLMObject subclasses. " - @"See https://realm.io/docs/objc/latest/#to-many for more information.", _name, _objectClassName); + if ([RLMSchema classForString:_objectClassName]) { + _optional = false; + _type = RLMPropertyTypeObject; + return YES; } + @throw RLMException(@"Property '%@' is of type 'RLMArray<%@>' which is not a supported RLMArray object type. " + @"RLMArrays can only contain instances of RLMObject subclasses. " + @"See https://realm.io/docs/objc/latest/#to-many for more information.", _name, _objectClassName); } else if (strncmp(code, numberPrefix, numberPrefixLen) == 0) { - // get number type from type string - @"NSNumber" - NSString *numberType = [[NSString alloc] initWithBytes:code + numberPrefixLen - length:strlen(code + numberPrefixLen) - 2 // drop trailing >" - encoding:NSUTF8StringEncoding]; - - if ([numberType isEqualToString:@"RLMInt"]) { - _type = RLMPropertyTypeInt; - } - else if ([numberType isEqualToString:@"RLMFloat"]) { - _type = RLMPropertyTypeFloat; - } - else if ([numberType isEqualToString:@"RLMDouble"]) { - _type = RLMPropertyTypeDouble; - } - else if ([numberType isEqualToString:@"RLMBool"]) { - _type = RLMPropertyTypeBool; - } - else { - @throw RLMException(@"Property '%@' is of type 'NSNumber<%@>' which is not a supported NSNumber object type. " - @"NSNumbers can only be RLMInt, RLMFloat, RLMDouble, and RLMBool at the moment. " - @"See https://realm.io/docs/objc/latest for more information.", _name, numberType); + auto type = typeFromProtocolString(code + numberPrefixLen); + if (type && (*type == RLMPropertyTypeInt || *type == RLMPropertyTypeFloat || *type == RLMPropertyTypeDouble || *type == RLMPropertyTypeBool)) { + _type = *type; + return YES; } + @throw RLMException(@"Property '%@' is of type %s which is not a supported NSNumber object type. " + @"NSNumbers can only be RLMInt, RLMFloat, RLMDouble, and RLMBool at the moment. " + @"See https://realm.io/docs/objc/latest for more information.", _name, code + 1); } else if (strncmp(code, linkingObjectsPrefix, linkingObjectsPrefixLen) == 0 && (code[linkingObjectsPrefixLen] == '"' || code[linkingObjectsPrefixLen] == '<')) { _type = RLMPropertyTypeLinkingObjects; _optional = false; + _array = true; if (!_objectClassName || !_linkOriginPropertyName) { @throw RLMException(@"Property '%@' is of type RLMLinkingObjects but +linkingObjectsProperties did not specify the class " @@ -265,10 +298,14 @@ - (BOOL)setTypeFromRawType:(NSString *)rawType { return YES; } -- (void)parseObjcProperty:(objc_property_t)property readOnly:(bool *)readOnly rawType:(NSString **)rawType { +- (void)parseObjcProperty:(objc_property_t)property + readOnly:(bool *)readOnly + computed:(bool *)computed + rawType:(NSString **)rawType { unsigned int count; objc_property_attribute_t *attrs = property_copyAttributeList(property, &count); + *computed = true; for (size_t i = 0; i < count; ++i) { switch (*attrs[i].name) { case 'T': @@ -289,6 +326,9 @@ - (void)parseObjcProperty:(objc_property_t)property readOnly:(bool *)readOnly ra case 'S': _setterName = @(attrs[i].value); break; + case 'V': // backing ivar name + *computed = false; + break; default: break; } @@ -318,8 +358,17 @@ - (instancetype)initSwiftPropertyWithName:(NSString *)name NSString *rawType; bool readOnly = false; - [self parseObjcProperty:property readOnly:&readOnly rawType:&rawType]; - if (readOnly) { + bool isComputed = false; + [self parseObjcProperty:property readOnly:&readOnly computed:&isComputed rawType:&rawType]; + if (!readOnly && isComputed) { + // Check for lazy property. + NSString *backingPropertyName = [NSString stringWithFormat:@"%@.storage", name]; + if (class_getInstanceVariable([obj class], backingPropertyName.UTF8String)) { + isComputed = false; + } + } + + if (readOnly || isComputed) { return nil; } @@ -342,42 +391,44 @@ - (instancetype)initSwiftPropertyWithName:(NSString *)name // convert array types to objc variant if ([rawType isEqualToString:@"@\"RLMArray\""]) { - rawType = [NSString stringWithFormat:@"@\"RLMArray<%@>\"", [propertyValue objectClassName]]; + RLMArray *value = propertyValue; + _type = value.type; + _optional = value.optional; + _array = true; + _objectClassName = value.objectClassName; + if (_type == RLMPropertyTypeObject && ![RLMSchema classForString:_objectClassName]) { + @throw RLMException(@"Property '%@' is of type 'RLMArray<%@>' which is not a supported RLMArray object type. " + @"RLMArrays can only contain instances of RLMObject subclasses. " + @"See https://realm.io/docs/objc/latest/#to-many for more information.", _name, _objectClassName); + } } else if ([rawType isEqualToString:@"@\"NSNumber\""]) { const char *numberType = [propertyValue objCType]; if (!numberType) { @throw RLMException(@"Can't persist NSNumber without default value: use a Swift-native number type or provide a default value."); } + _optional = true; switch (*numberType) { - case 'i': - case 'l': - case 'q': - rawType = @"@\"NSNumber\""; + case 'i': case 'l': case 'q': + _type = RLMPropertyTypeInt; break; case 'f': - rawType = @"@\"NSNumber\""; + _type = RLMPropertyTypeFloat; break; case 'd': - rawType = @"@\"NSNumber\""; + _type = RLMPropertyTypeDouble; break; - case 'B': - case 'c': - rawType = @"@\"NSNumber\""; + case 'B': case 'c': + _type = RLMPropertyTypeBool; break; default: @throw RLMException(@"Can't persist NSNumber of type '%s': only integers, floats, doubles, and bools are currently supported.", numberType); } } - - auto throwForPropertyName = ^(NSString *propertyName){ + else if (![self setTypeFromRawType:rawType]) { @throw RLMException(@"Can't persist property '%@' with incompatible type. " "Add to Object.ignoredProperties() class method to ignore.", - propertyName); - }; - - if (![self setTypeFromRawType:rawType]) { - throwForPropertyName(self.name); + self.name); } if ([rawType isEqualToString:@"c"]) { @@ -414,9 +465,10 @@ - (instancetype)initWithName:(NSString *)name NSString *rawType; bool isReadOnly = false; - [self parseObjcProperty:property readOnly:&isReadOnly rawType:&rawType]; - bool isComputedProperty = rawTypeIsComputedProperty(rawType); - if (isReadOnly && !isComputedProperty) { + bool isComputed = false; + [self parseObjcProperty:property readOnly:&isReadOnly computed:&isComputed rawType:&rawType]; + bool shouldBeTreatedAsComputedProperty = rawTypeShouldBeTreatedAsComputedProperty(rawType); + if ((isReadOnly || isComputed) && !shouldBeTreatedAsComputedProperty) { return nil; } @@ -425,7 +477,7 @@ - (instancetype)initWithName:(NSString *)name "Add to ignoredPropertyNames: method to ignore.", self.name); } - if (!isReadOnly && isComputedProperty) { + if (!isReadOnly && shouldBeTreatedAsComputedProperty) { @throw RLMException(@"Property '%@' must be declared as readonly as %@ properties cannot be written to.", self.name, RLMTypeToString(_type)); } @@ -437,17 +489,19 @@ - (instancetype)initWithName:(NSString *)name } - (instancetype)initSwiftListPropertyWithName:(NSString *)name - ivar:(Ivar)ivar - objectClassName:(NSString *)objectClassName { + instance:(id)object { self = [super init]; if (!self) { return nil; } - _name = name; - _type = RLMPropertyTypeArray; - _objectClassName = objectClassName; - _swiftIvar = ivar; + _array = true; + _swiftIvar = class_getInstanceVariable([object class], name.UTF8String); + + RLMArray *array = [object_getIvar(object, _swiftIvar) _rlmArray]; + _type = array.type; + _optional = array.optional; + _objectClassName = array.objectClassName; // no obj-c property for generic lists, and thus no getter/setter names @@ -485,6 +539,7 @@ - (instancetype)initSwiftLinkingObjectsPropertyWithName:(NSString *)name _name = name; _type = RLMPropertyTypeLinkingObjects; + _array = true; _objectClassName = objectClassName; _linkOriginPropertyName = linkOriginPropertyName; _swiftIvar = ivar; @@ -497,8 +552,10 @@ - (instancetype)initSwiftLinkingObjectsPropertyWithName:(NSString *)name - (id)copyWithZone:(NSZone *)zone { RLMProperty *prop = [[RLMProperty allocWithZone:zone] init]; prop->_name = _name; + prop->_columnName = _columnName; prop->_type = _type; prop->_objectClassName = _objectClassName; + prop->_array = _array; prop->_indexed = _indexed; prop->_getterName = _getterName; prop->_setterName = _setterName; @@ -533,21 +590,51 @@ - (BOOL)isEqualToProperty:(RLMProperty *)property { && _optional == property->_optional && [_name isEqualToString:property->_name] && (_objectClassName == property->_objectClassName || [_objectClassName isEqualToString:property->_objectClassName]) - && (_linkOriginPropertyName == property->_linkOriginPropertyName || [_linkOriginPropertyName isEqualToString:property->_linkOriginPropertyName]); + && (_linkOriginPropertyName == property->_linkOriginPropertyName || + [_linkOriginPropertyName isEqualToString:property->_linkOriginPropertyName]); } - (NSString *)description { - return [NSString stringWithFormat:@"%@ {\n\ttype = %@;\n\tobjectClassName = %@;\n\tlinkOriginPropertyName = %@;\n\tindexed = %@;\n\tisPrimary = %@;\n\toptional = %@;\n}", self.name, RLMTypeToString(self.type), self.objectClassName, self.linkOriginPropertyName, self.indexed ? @"YES" : @"NO", self.isPrimary ? @"YES" : @"NO", self.optional ? @"YES" : @"NO"]; + return [NSString stringWithFormat: + @"%@ {\n" + "\ttype = %@;\n" + "\tobjectClassName = %@;\n" + "\tlinkOriginPropertyName = %@;\n" + "\tindexed = %@;\n" + "\tisPrimary = %@;\n" + "\tarray = %@;\n" + "\toptional = %@;\n" + "}", + self.name, RLMTypeToString(self.type), self.objectClassName, + self.linkOriginPropertyName, + self.indexed ? @"YES" : @"NO", + self.isPrimary ? @"YES" : @"NO", + self.array ? @"YES" : @"NO", + self.optional ? @"YES" : @"NO"]; } -- (realm::Property)objectStoreCopy { +- (NSString *)columnName { + return _columnName ?: _name; +} + +- (realm::Property)objectStoreCopy:(RLMSchema *)schema { realm::Property p; - p.name = _name.UTF8String; - p.type = (realm::PropertyType)_type; - p.object_type = _objectClassName ? _objectClassName.UTF8String : ""; - p.is_indexed = _indexed; - p.is_nullable = _optional; - p.link_origin_property_name = _linkOriginPropertyName ? _linkOriginPropertyName.UTF8String : ""; + p.name = self.columnName.UTF8String; + if (_objectClassName) { + RLMObjectSchema *targetSchema = schema[_objectClassName]; + p.object_type = (targetSchema.objectName ?: _objectClassName).UTF8String; + if (_linkOriginPropertyName) { + p.link_origin_property_name = (targetSchema[_linkOriginPropertyName].columnName ?: _linkOriginPropertyName).UTF8String; + } + } + p.is_indexed = static_cast(_indexed); + p.type = static_cast(_type); + if (_array) { + p.type |= realm::PropertyType::Array; + } + if (_optional) { + p.type |= realm::PropertyType::Nullable; + } return p; } diff --git a/Pods/Realm/Realm/RLMQueryUtil.mm b/Pods/Realm/Realm/RLMQueryUtil.mm index e1ac2f1..11e3884 100644 --- a/Pods/Realm/Realm/RLMQueryUtil.mm +++ b/Pods/Realm/Realm/RLMQueryUtil.mm @@ -30,6 +30,9 @@ #import "results.hpp" #include +#include +#include +#include using namespace realm; @@ -85,34 +88,83 @@ BOOL RLMPropertyTypeIsNumeric(RLMPropertyType propertyType) { } } -// FIXME: TrueExpression and FalseExpression should be supported by core in some way +// Equal and ContainsSubstring are used by QueryBuilder::add_string_constraint as the comparator +// for performing diacritic-insensitive comparisons. -struct TrueExpression : realm::Expression { - size_t find_first(size_t start, size_t end) const override - { - if (start != end) - return start; - - return realm::not_found; +bool equal(CFStringCompareFlags options, StringData v1, StringData v2) +{ + if (v1.is_null() || v2.is_null()) { + return v1.is_null() == v2.is_null(); } - void set_base_table(const Table*) override {} - const Table* get_base_table() const override { return nullptr; } - std::unique_ptr clone(QueryNodeHandoverPatches*) const override + + auto s1 = util::adoptCF(CFStringCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (const UInt8*)v1.data(), v1.size(), + kCFStringEncodingUTF8, false, kCFAllocatorNull)); + auto s2 = util::adoptCF(CFStringCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (const UInt8*)v2.data(), v2.size(), + kCFStringEncodingUTF8, false, kCFAllocatorNull)); + + return CFStringCompare(s1.get(), s2.get(), options) == kCFCompareEqualTo; +} + +template +struct Equal { + using CaseSensitive = Equal; + using CaseInsensitive = Equal; + + bool operator()(StringData v1, StringData v2, bool v1_null, bool v2_null) const { - return std::unique_ptr(new TrueExpression(*this)); + REALM_ASSERT_DEBUG(v1_null == v1.is_null()); + REALM_ASSERT_DEBUG(v2_null == v2.is_null()); + + return equal(options, v1, v2); } + + // FIXME: Consider the options. + static const char* description() { return "equal"; } }; -struct FalseExpression : realm::Expression { - size_t find_first(size_t, size_t) const override { return realm::not_found; } - void set_base_table(const Table*) override {} - const Table* get_base_table() const override { return nullptr; } - std::unique_ptr clone(QueryNodeHandoverPatches*) const override +bool contains_substring(CFStringCompareFlags options, StringData v1, StringData v2) +{ + if (v2.is_null()) { + // Everything contains NULL + return true; + } + + if (v1.is_null()) { + // NULL contains nothing (except NULL, handled above) + return false; + } + + if (v2.size() == 0) { + // Everything (except NULL, handled above) contains the empty string + return true; + } + + auto s1 = util::adoptCF(CFStringCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (const UInt8*)v1.data(), v1.size(), + kCFStringEncodingUTF8, false, kCFAllocatorNull)); + auto s2 = util::adoptCF(CFStringCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (const UInt8*)v2.data(), v2.size(), + kCFStringEncodingUTF8, false, kCFAllocatorNull)); + + return CFStringFind(s1.get(), s2.get(), options).location != kCFNotFound; +} + +template +struct ContainsSubstring { + using CaseSensitive = ContainsSubstring; + using CaseInsensitive = ContainsSubstring; + + bool operator()(StringData v1, StringData v2, bool v1_null, bool v2_null) const { - return std::unique_ptr(new FalseExpression(*this)); + REALM_ASSERT_DEBUG(v1_null == v1.is_null()); + REALM_ASSERT_DEBUG(v2_null == v2.is_null()); + + return contains_substring(options, v1, v2); } + + // FIXME: Consider the options. + static const char* description() { return "contains"; } }; + NSString *operatorName(NSPredicateOperatorType operatorType) { switch (operatorType) { @@ -161,7 +213,7 @@ void set_base_table(const Table*) override {} : m_links(links), m_property(property), m_schema(schema), m_group(&group), m_query(&query), m_table(query.get_table().get()) { auto& table = walk_link_chain([](Table&, size_t, RLMPropertyType) { }); - m_index = table.get_column_index(m_property.name.UTF8String); + m_index = table.get_column_index(m_property.columnName.UTF8String); } template @@ -186,20 +238,18 @@ auto resolve(SubQuery&&... subquery) const { switch (type()) { case RLMPropertyTypeObject: - case RLMPropertyTypeArray: case RLMPropertyTypeLinkingObjects: return m_schema[property().objectClassName]; default: - REALM_ASSERT(false); + REALM_UNREACHABLE(); } } bool has_links() const { return m_links.size(); } bool has_any_to_many_links() const { - return std::any_of(begin(m_links), end(m_links), [](RLMProperty *property) { - return property.type == RLMPropertyTypeArray || property.type == RLMPropertyTypeLinkingObjects; - }); + return std::any_of(begin(m_links), end(m_links), + [](RLMProperty *property) { return property.array; }); } ColumnReference last_link_column() const { @@ -242,7 +292,7 @@ auto do_resolve_backlink(std::true_type, SubQuery&&... subquery) const auto table = m_query->get_table().get(); for (const auto& link : m_links) { if (link.type != RLMPropertyTypeLinkingObjects) { - auto index = table->get_column_index(link.name.UTF8String); + auto index = table->get_column_index(link.columnName.UTF8String); func(*table, index, link.type); table = table->get_link_target(index).get(); } @@ -261,7 +311,8 @@ auto with_link_origin(RLMProperty *prop, Func&& func) const { RLMObjectSchema *link_origin_schema = m_schema[prop.objectClassName]; Table& link_origin_table = get_table(*m_group, link_origin_schema); - size_t link_origin_column = link_origin_table.get_column_index(prop.linkOriginPropertyName.UTF8String); + NSString *column_name = link_origin_schema[prop.linkOriginPropertyName].columnName; + size_t link_origin_column = link_origin_table.get_column_index(column_name.UTF8String); return func(link_origin_table, link_origin_column); } @@ -301,7 +352,7 @@ void set_link_chain_on_table() const , m_link_column(std::move(link_column)) , m_column(std::move(column)) { - RLMPrecondition(m_link_column.type() == RLMPropertyTypeArray || m_link_column.type() == RLMPropertyTypeLinkingObjects, + RLMPrecondition(m_link_column.property().array, @"Invalid predicate", @"Collection operation can only be applied to a property of type RLMArray."); switch (m_type) { @@ -423,6 +474,12 @@ void add_numeric_constraint(RLMPropertyType datatype, template void add_bool_constraint(NSPredicateOperatorType operatorType, A lhs, B rhs); + void add_substring_constraint(null, Query condition); + template + void add_substring_constraint(const T& value, Query condition); + template + void add_substring_constraint(const Columns& value, Query condition); + template void add_string_constraint(NSPredicateOperatorType operatorType, NSComparisonPredicateOptions predicateOptions, @@ -446,9 +503,9 @@ void do_add_constraint(RLMPropertyType type, NSPredicateOperatorType operatorTyp void add_between_constraint(const ColumnReference& column, id value); - template - void add_binary_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, T value); + void add_binary_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, BinaryData value); void add_binary_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, id value); + void add_binary_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, null); void add_binary_constraint(NSPredicateOperatorType operatorType, id value, const ColumnReference& column); void add_binary_constraint(NSPredicateOperatorType, const ColumnReference&, const ColumnReference&); @@ -501,7 +558,8 @@ void add_collection_operation_constraint(NSPredicateOperatorType operatorType, break; default: @throw RLMPredicateException(@"Invalid operator type", - @"Operator '%@' not supported for type %@", operatorName(operatorType), RLMTypeToString(datatype)); + @"Operator '%@' not supported for type %@", + operatorName(operatorType), RLMTypeToString(datatype)); } } @@ -520,35 +578,105 @@ void add_collection_operation_constraint(NSPredicateOperatorType operatorType, } } +void QueryBuilder::add_substring_constraint(null, Query) { + // Foundation always returns false for substring operations with a RHS of null or "". + m_query.and_query(std::unique_ptr(new FalseExpression)); +} + +template +void QueryBuilder::add_substring_constraint(const T& value, Query condition) { + // Foundation always returns false for substring operations with a RHS of null or "". + m_query.and_query(value.size() + ? std::move(condition) + : std::unique_ptr(new FalseExpression)); +} + +template +void QueryBuilder::add_substring_constraint(const Columns& value, Query condition) { + // Foundation always returns false for substring operations with a RHS of null or "". + // We don't need to concern ourselves with the possibility of value traversing a link list + // and producing multiple values per row as such expressions will have been rejected. + m_query.and_query(const_cast&>(value).size() != 0 && std::move(condition)); +} + template void QueryBuilder::add_string_constraint(NSPredicateOperatorType operatorType, NSComparisonPredicateOptions predicateOptions, Columns &&column, T value) { bool caseSensitive = !(predicateOptions & NSCaseInsensitivePredicateOption); - bool diacriticInsensitive = (predicateOptions & NSDiacriticInsensitivePredicateOption); - RLMPrecondition(!diacriticInsensitive, @"Invalid predicate option", - @"NSDiacriticInsensitivePredicateOption not supported for string type"); + bool diacriticSensitive = !(predicateOptions & NSDiacriticInsensitivePredicateOption); + + if (diacriticSensitive) { + switch (operatorType) { + case NSBeginsWithPredicateOperatorType: + add_substring_constraint(value, column.begins_with(value, caseSensitive)); + break; + case NSEndsWithPredicateOperatorType: + add_substring_constraint(value, column.ends_with(value, caseSensitive)); + break; + case NSContainsPredicateOperatorType: + add_substring_constraint(value, column.contains(value, caseSensitive)); + break; + case NSEqualToPredicateOperatorType: + m_query.and_query(column.equal(value, caseSensitive)); + break; + case NSNotEqualToPredicateOperatorType: + m_query.and_query(column.not_equal(value, caseSensitive)); + break; + case NSLikePredicateOperatorType: + m_query.and_query(column.like(value, caseSensitive)); + break; + default: + @throw RLMPredicateException(@"Invalid operator type", + @"Operator '%@' not supported for string type", + operatorName(operatorType)); + } + return; + } + + auto as_subexpr = util::overload([](StringData value) { return make_subexpr(value); }, + [](const Columns& c) { return c.clone(); }); + auto left = as_subexpr(column); + auto right = as_subexpr(value); + + auto make_constraint = [&](auto comparator) { + using Comparator = decltype(comparator); + using CompareCS = Compare; + using CompareCI = Compare; + if (caseSensitive) { + return make_expression(std::move(left), std::move(right)); + } + else { + return make_expression(std::move(left), std::move(right)); + } + }; switch (operatorType) { - case NSBeginsWithPredicateOperatorType: - m_query.and_query(column.begins_with(value, caseSensitive)); - break; - case NSEndsWithPredicateOperatorType: - m_query.and_query(column.ends_with(value, caseSensitive)); + case NSBeginsWithPredicateOperatorType: { + using C = ContainsSubstring; + add_substring_constraint(value, make_constraint(C{})); break; - case NSContainsPredicateOperatorType: - m_query.and_query(column.contains(value, caseSensitive)); + } + case NSEndsWithPredicateOperatorType: { + using C = ContainsSubstring; + add_substring_constraint(value, make_constraint(C{})); break; - case NSEqualToPredicateOperatorType: - m_query.and_query(column.equal(value, caseSensitive)); + } + case NSContainsPredicateOperatorType: { + using C = ContainsSubstring; + add_substring_constraint(value, make_constraint(C{})); break; + } case NSNotEqualToPredicateOperatorType: - m_query.and_query(column.not_equal(value, caseSensitive)); + m_query.Not(); + REALM_FALLTHROUGH; + case NSEqualToPredicateOperatorType: + m_query.and_query(make_constraint(Equal{})); break; case NSLikePredicateOperatorType: - m_query.and_query(column.like(value, caseSensitive)); - break; + @throw RLMPredicateException(@"Invalid operator type", + @"Operator 'LIKE' not supported with diacritic-insensitive modifier."); default: @throw RLMPredicateException(@"Invalid operator type", @"Operator '%@' not supported for string type", operatorName(operatorType)); @@ -614,22 +742,23 @@ void validate_and_extract_between_range(id value, RLMProperty *prop, id *from, i m_query.end_group(); } -template void QueryBuilder::add_binary_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, - T value) { + BinaryData value) { RLMPrecondition(!column.has_links(), @"Unsupported operator", @"NSData properties cannot be queried over an object link."); size_t index = column.index(); + Query query = m_query.get_table()->where(); + switch (operatorType) { case NSBeginsWithPredicateOperatorType: - m_query.begins_with(index, value); + add_substring_constraint(value, query.begins_with(index, value)); break; case NSEndsWithPredicateOperatorType: - m_query.ends_with(index, value); + add_substring_constraint(value, query.ends_with(index, value)); break; case NSContainsPredicateOperatorType: - m_query.contains(index, value); + add_substring_constraint(value, query.contains(index, value)); break; case NSEqualToPredicateOperatorType: m_query.equal(index, value); @@ -647,6 +776,10 @@ void validate_and_extract_between_range(id value, RLMProperty *prop, id *from, i add_binary_constraint(operatorType, column, RLMBinaryDataForNSData(value)); } +void QueryBuilder::add_binary_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, null) { + add_binary_constraint(operatorType, column, BinaryData()); +} + void QueryBuilder::add_binary_constraint(NSPredicateOperatorType operatorType, id value, const ColumnReference& column) { switch (operatorType) { case NSEqualToPredicateOperatorType: @@ -680,14 +813,15 @@ void validate_and_extract_between_range(id value, RLMProperty *prop, id *from, i void QueryBuilder::add_link_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, realm::null) { - RLMPrecondition(!column.has_links(), @"Unsupported operator", @"Multi-level object equality link queries are not supported."); RLMPrecondition(operatorType == NSEqualToPredicateOperatorType || operatorType == NSNotEqualToPredicateOperatorType, @"Invalid operator type", @"Only 'Equal' and 'Not Equal' operators supported for object comparison"); - if (operatorType == NSNotEqualToPredicateOperatorType) { - m_query.Not(); - } - m_query.and_query(column.resolve().is_null()); + if (operatorType == NSEqualToPredicateOperatorType) { + m_query.and_query(column.resolve() == null()); + } + else { + m_query.and_query(column.resolve() != null()); + } } template @@ -811,7 +945,6 @@ auto value_of_type(const ColumnReference& column) { add_binary_constraint(operatorType, values...); break; case RLMPropertyTypeObject: - case RLMPropertyTypeArray: case RLMPropertyTypeLinkingObjects: add_link_constraint(operatorType, values...); break; @@ -873,14 +1006,16 @@ KeyPath key_path_from_string(RLMSchema *schema, RLMObjectSchema *objectSchema, N NSString *propertyName = [keyPath substringWithRange:{start, end == NSNotFound ? length - start : end - start}]; property = objectSchema[propertyName]; RLMPrecondition(property, @"Invalid property name", - @"Property '%@' not found in object of type '%@'", propertyName, objectSchema.className); + @"Property '%@' not found in object of type '%@'", + propertyName, objectSchema.className); - if (property.type == RLMPropertyTypeArray || property.type == RLMPropertyTypeLinkingObjects) + if (property.array) keyPathContainsToManyRelationship = true; if (end != NSNotFound) { - RLMPrecondition(property.type == RLMPropertyTypeObject || property.type == RLMPropertyTypeArray || property.type == RLMPropertyTypeLinkingObjects, - @"Invalid value", @"Property '%@' is not a link in object of type '%@'", propertyName, objectSchema.className); + RLMPrecondition(property.type == RLMPropertyTypeObject || property.type == RLMPropertyTypeLinkingObjects, + @"Invalid value", @"Property '%@' is not a link in object of type '%@'", + propertyName, objectSchema.className); links.push_back(property); REALM_ASSERT(property.objectClassName); @@ -893,7 +1028,8 @@ KeyPath key_path_from_string(RLMSchema *schema, RLMObjectSchema *objectSchema, N return {std::move(links), property, keyPathContainsToManyRelationship}; } -ColumnReference QueryBuilder::column_reference_from_key_path(RLMObjectSchema *objectSchema, NSString *keyPathString, bool isAggregate) +ColumnReference QueryBuilder::column_reference_from_key_path(RLMObjectSchema *objectSchema, + NSString *keyPathString, bool isAggregate) { auto keyPath = key_path_from_string(m_schema, objectSchema, keyPathString); @@ -914,7 +1050,7 @@ void validate_property_value(const ColumnReference& column, __unsafe_unretained RLMObjectSchema *const objectSchema, __unsafe_unretained NSString *const keyPath) { RLMProperty *prop = column.property(); - if (prop.type == RLMPropertyTypeArray) { + if (prop.array) { RLMPrecondition([RLMObjectBaseObjectSchema(RLMDynamicCast(value)).className isEqualToString:prop.objectClassName], @"Invalid value", err, prop.objectClassName, keyPath, objectSchema.className, value); } @@ -1016,7 +1152,8 @@ bool key_path_contains_collection_operator(NSString *keyPath) { return [keyPath rangeOfString:@"@"].location != NSNotFound; } -NSString *get_collection_operation_name_from_key_path(NSString *keyPath, NSString **leadingKeyPath, NSString **trailingKey) { +NSString *get_collection_operation_name_from_key_path(NSString *keyPath, NSString **leadingKeyPath, + NSString **trailingKey) { NSRange at = [keyPath rangeOfString:@"@"]; if (at.location == NSNotFound || at.location >= keyPath.length - 1) { @throw RLMPredicateException(@"Invalid key path", @"'%@' is not a valid key path'", keyPath); @@ -1329,47 +1466,6 @@ bool is_self_value_for_key_path_function_expression(NSExpression *expression) @"Only support compound, comparison, and constant predicates"); } } - -std::vector RLMValidatedColumnIndicesForSort(RLMClassInfo& classInfo, NSString *keyPathString) -{ - RLMPrecondition([keyPathString rangeOfString:@"@"].location == NSNotFound, @"Invalid key path for sort", - @"Cannot sort on '%@': sorting on key paths that include collection operators is not supported.", - keyPathString); - auto keyPath = key_path_from_string(classInfo.realm.schema, classInfo.rlmObjectSchema, keyPathString); - - RLMPrecondition(!keyPath.containsToManyRelationship, @"Invalid key path for sort", - @"Cannot sort on '%@': sorting on key paths that include a to-many relationship is not supported.", - keyPathString); - - switch (keyPath.property.type) { - case RLMPropertyTypeBool: - case RLMPropertyTypeDate: - case RLMPropertyTypeDouble: - case RLMPropertyTypeFloat: - case RLMPropertyTypeInt: - case RLMPropertyTypeString: - break; - - default: - @throw RLMPredicateException(@"Invalid sort property type", - @"Cannot sort on key path '%@' on object of type '%s': sorting is only supported on bool, date, double, float, integer, and string properties, but property is of type %@.", - keyPathString, classInfo.rlmObjectSchema.className, RLMTypeToString(keyPath.property.type)); - } - - std::vector columnIndices; - columnIndices.reserve(keyPath.links.size() + 1); - - auto currentClassInfo = &classInfo; - for (RLMProperty *link : keyPath.links) { - auto tableColumn = currentClassInfo->tableColumn(link); - currentClassInfo = ¤tClassInfo->linkTargetType(link.index); - columnIndices.push_back(tableColumn); - } - columnIndices.push_back(currentClassInfo->tableColumn(keyPath.property)); - - return columnIndices; -} - } // namespace realm::Query RLMPredicateToQuery(NSPredicate *predicate, RLMObjectSchema *objectSchema, @@ -1392,17 +1488,3 @@ bool is_self_value_for_key_path_function_expression(NSExpression *expression) (int)validateMessage.size(), validateMessage.c_str()); return query; } - -realm::SortDescriptor RLMSortDescriptorFromDescriptors(RLMClassInfo& classInfo, NSArray *descriptors) { - std::vector> columnIndices; - std::vector ascending; - columnIndices.reserve(descriptors.count); - ascending.reserve(descriptors.count); - - for (RLMSortDescriptor *descriptor in descriptors) { - columnIndices.push_back(RLMValidatedColumnIndicesForSort(classInfo, descriptor.keyPath)); - ascending.push_back(descriptor.ascending); - } - - return {*classInfo.table(), std::move(columnIndices), std::move(ascending)}; -} diff --git a/Pods/Realm/Realm/RLMRealm+Sync.mm b/Pods/Realm/Realm/RLMRealm+Sync.mm new file mode 100644 index 0000000..5cf9bc4 --- /dev/null +++ b/Pods/Realm/Realm/RLMRealm+Sync.mm @@ -0,0 +1,59 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2017 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMRealm+Sync.h" + +#import "RLMObjectBase.h" +#import "RLMObjectSchema.h" +#import "RLMRealm_Private.hpp" +#import "RLMResults_Private.hpp" +#import "RLMSchema.h" +#import "RLMSyncSession.h" + +#import "results.hpp" +#import "sync/partial_sync.hpp" +#import "shared_realm.hpp" + +using namespace realm; + +@implementation RLMRealm (Sync) + +- (void)subscribeToObjects:(Class)type where:(NSString *)query callback:(RLMPartialSyncFetchCallback)callback { + NSString *className = [type className]; + auto cb = [=](Results results, std::exception_ptr err) { + if (err) { + try { + rethrow_exception(err); + } + catch (...) { + NSError *error = nil; + RLMRealmTranslateException(&error); + callback(nil, error); + } + return; + } + callback([RLMResults resultsWithObjectInfo:_info[className] results:std::move(results)], nil); + }; + partial_sync::register_query(_realm, className.UTF8String, query.UTF8String, std::move(cb)); +} + +- (RLMSyncSession *)syncSession { + return [RLMSyncSession sessionForRealm:self]; +} + +@end diff --git a/Pods/Realm/Realm/RLMRealm.mm b/Pods/Realm/Realm/RLMRealm.mm index 0dd0116..c5be182 100644 --- a/Pods/Realm/Realm/RLMRealm.mm +++ b/Pods/Realm/Realm/RLMRealm.mm @@ -20,19 +20,20 @@ #import "RLMAnalytics.hpp" #import "RLMArray_Private.hpp" -#import "RLMRealmConfiguration_Private.hpp" #import "RLMMigration_Private.h" -#import "RLMObjectSchema_Private.hpp" -#import "RLMProperty_Private.h" -#import "RLMObjectStore.h" #import "RLMObject_Private.h" #import "RLMObject_Private.hpp" +#import "RLMObjectSchema_Private.hpp" +#import "RLMObjectStore.h" #import "RLMObservation.hpp" #import "RLMProperty.h" +#import "RLMProperty_Private.h" #import "RLMQueryUtil.hpp" +#import "RLMRealmConfiguration_Private.hpp" #import "RLMRealmUtil.hpp" #import "RLMSchema_Private.hpp" #import "RLMSyncManager_Private.h" +#import "RLMSyncUtil_Private.hpp" #import "RLMThreadSafeReference_Private.hpp" #import "RLMUpdateChecker.hpp" #import "RLMUtil.hpp" @@ -46,6 +47,8 @@ #include #include +#import "sync/sync_session.hpp" + using namespace realm; using util::File; @@ -63,8 +66,12 @@ void RLMDisableSyncToDisk() { realm::disable_sync_to_disk(); } +static void RLMAddSkipBackupAttributeToItemAtPath(std::string const& path) { + [[NSURL fileURLWithPath:@(path.c_str())] setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil]; +} + @implementation RLMRealmNotificationToken -- (void)stop { +- (void)invalidate { [_realm verifyThread]; [_realm.notificationHandlers removeObject:self]; _realm = nil; @@ -90,7 +97,7 @@ - (void)dealloc { if (_realm || _block) { NSLog(@"RLMNotificationToken released without unregistering a notification. You must hold " @"on to the RLMNotificationToken returned from addNotificationBlock and call " - @"-[RLMNotificationToken stop] when you no longer wish to receive RLMRealm notifications."); + @"-[RLMNotificationToken invalidate] when you no longer wish to receive RLMRealm notifications."); } } @end @@ -105,13 +112,8 @@ static bool shouldForciblyDisableEncryption() { return nil; } - if (key) { - if (key.length != 64) { - @throw RLMException(@"Encryption key must be exactly 64 bytes long"); - } -#if TARGET_OS_WATCH - @throw RLMException(@"Cannot open an encrypted Realm on watchOS."); -#endif + if (key && key.length != 64) { + @throw RLMException(@"Encryption key must be exactly 64 bytes long"); } return key; @@ -137,12 +139,22 @@ + (void)initialize { RLMSendAnalytics(); } +- (instancetype)initPrivate { + self = [super init]; + return self; +} + - (BOOL)isEmpty { return realm::ObjectStore::is_empty(self.group); } - (void)verifyThread { - _realm->verify_thread(); + try { + _realm->verify_thread(); + } + catch (std::exception const& e) { + @throw RLMException(e); + } } - (BOOL)inWriteTransaction { @@ -170,24 +182,86 @@ + (instancetype)realmWithURL:(NSURL *)fileURL { configuration.fileURL = fileURL; return [RLMRealm realmWithConfiguration:configuration error:nil]; } + ++ (void)asyncOpenWithConfiguration:(RLMRealmConfiguration *)configuration + callbackQueue:(dispatch_queue_t)callbackQueue + callback:(RLMAsyncOpenRealmCallback)callback { + RLMRealm *strongReferenceToSyncedRealm = nil; + if (configuration.config.sync_config) { + NSError *error = nil; + strongReferenceToSyncedRealm = [RLMRealm uncachedSchemalessRealmWithConfiguration:configuration error:&error]; + if (error) { + dispatch_async(callbackQueue, ^{ + callback(nil, error); + }); + return; + } + } + static dispatch_queue_t queue = dispatch_queue_create("io.realm.asyncOpenDispatchQueue", DISPATCH_QUEUE_CONCURRENT); + dispatch_async(queue, ^{ + @autoreleasepool { + if (strongReferenceToSyncedRealm) { + // Sync behavior: get the raw session, then wait for it to download. + if (auto session = sync_session_for_realm(strongReferenceToSyncedRealm)) { + // Wait for the session to download, then open it. + session->wait_for_download_completion([=](std::error_code error_code) { + dispatch_async(callbackQueue, ^{ + (void)strongReferenceToSyncedRealm; + NSError *error = nil; + if (error_code == std::error_code{}) { + // Success + @autoreleasepool { + // Try opening the Realm on the destination queue. + RLMRealm *localRealm = [RLMRealm realmWithConfiguration:configuration error:&error]; + callback(localRealm, error); + } + } else { + // Failure + callback(nil, make_sync_error(RLMSyncSystemErrorKindSession, + @(error_code.message().c_str()), + error_code.value(), + nil)); + } + }); + }); + } else { + dispatch_async(callbackQueue, ^{ + callback(nil, make_sync_error(RLMSyncSystemErrorKindSession, + @"Cannot asynchronously open synced Realm, because the associated session previously experienced a fatal error", + NSNotFound, + nil)); + }); + return; + } + } else { + // Default behavior: just dispatch onto the destination queue and open the Realm. + dispatch_async(callbackQueue, ^{ + @autoreleasepool { + NSError *error = nil; + RLMRealm *localRealm = [RLMRealm realmWithConfiguration:configuration error:&error]; + callback(localRealm, error); + } + }); + return; + } + } + }); +} + // ARC tries to eliminate calls to autorelease when the value is then immediately // returned, but this results in significantly different semantics between debug // and release builds for RLMRealm, so force it to always autorelease. -static id RLMAutorelease(id value) { +static id RLMAutorelease(__unsafe_unretained id value) { // +1 __bridge_retained, -1 CFAutorelease return value ? (__bridge id)CFAutorelease((__bridge_retained CFTypeRef)value) : nil; } -static void RLMRealmSetSchemaAndAlign(RLMRealm *realm, RLMSchema *targetSchema) { - realm.schema = targetSchema; - realm->_info = RLMSchemaInfo(realm, targetSchema, realm->_realm->schema()); -} - + (instancetype)realmWithSharedRealm:(SharedRealm)sharedRealm schema:(RLMSchema *)schema { - RLMRealm *realm = [RLMRealm new]; + RLMRealm *realm = [[RLMRealm alloc] initPrivate]; realm->_realm = sharedRealm; realm->_dynamic = YES; - RLMRealmSetSchemaAndAlign(realm, schema); + realm->_schema = schema; + realm->_info = RLMSchemaInfo(realm); return RLMAutorelease(realm); } @@ -252,18 +326,50 @@ REALM_NOINLINE void RLMRealmTranslateException(NSError **error) { } } +REALM_NOINLINE static void translateSharedGroupOpenException(RLMRealmConfiguration *originalConfiguration, NSError **error) { + try { + throw; + } + catch (RealmFileException const& ex) { + switch (ex.kind()) { + case RealmFileException::Kind::IncompatibleSyncedRealm: { + RLMRealmConfiguration *configuration = [originalConfiguration copy]; + configuration.fileURL = [NSURL fileURLWithPath:@(ex.path().data())]; + configuration.readOnly = YES; + + NSError *intermediateError = RLMMakeError(RLMErrorIncompatibleSyncedFile, ex); + NSMutableDictionary *userInfo = [intermediateError.userInfo mutableCopy]; + userInfo[RLMBackupRealmConfigurationErrorKey] = configuration; + NSError *finalError = [NSError errorWithDomain:intermediateError.domain code:intermediateError.code + userInfo:userInfo]; + RLMSetErrorOrThrow(finalError, error); + break; + } + default: + RLMRealmTranslateException(error); + break; + } + } + catch (...) { + RLMRealmTranslateException(error); + } +} + + + (instancetype)realmWithConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error { bool dynamic = configuration.dynamic; + bool cache = configuration.cache; bool readOnly = configuration.readOnly; { Realm::Config& config = configuration.config; // try to reuse existing realm first - if (config.cache || dynamic) { + if (cache || dynamic) { if (RLMRealm *realm = RLMGetThreadLocalCachedRealmForPath(config.path)) { auto const& old_config = realm->_realm->config(); - if (old_config.read_only() != config.read_only()) { + if (old_config.immutable() != config.immutable() + || old_config.read_only_alternative() != config.read_only_alternative()) { @throw RLMException(@"Realm at path '%s' already opened with different read permissions", config.path.c_str()); } if (old_config.in_memory != config.in_memory) { @@ -283,33 +389,36 @@ + (instancetype)realmWithConfiguration:(RLMRealmConfiguration *)configuration er configuration = [configuration copy]; Realm::Config& config = configuration.config; - RLMRealm *realm = [RLMRealm new]; + RLMRealm *realm = [[RLMRealm alloc] initPrivate]; realm->_dynamic = dynamic; // protects the realm cache and accessors cache - static std::mutex initLock; + static std::mutex& initLock = *new std::mutex(); std::lock_guard lock(initLock); try { realm->_realm = Realm::get_shared_realm(config); } catch (...) { - RLMRealmTranslateException(error); + translateSharedGroupOpenException(configuration, error); return nil; } - RLMSchema *cachedRealmSchema; + // if we have a cached realm on another thread we can skip a few steps and + // just grab its schema @autoreleasepool { // ensure that cachedRealm doesn't end up in this thread's autorelease pool - cachedRealmSchema = RLMGetAnyCachedRealmForPath(config.path).schema; + if (auto cachedRealm = RLMGetAnyCachedRealmForPath(config.path)) { + realm->_realm->set_schema_subset(cachedRealm->_realm->schema()); + realm->_schema = cachedRealm.schema; + realm->_info = cachedRealm->_info.clone(cachedRealm->_realm->schema(), realm); + } } - // if we have a cached realm on another thread, copy without a transaction - if (cachedRealmSchema) { - RLMRealmSetSchemaAndAlign(realm, cachedRealmSchema); - } + if (realm->_schema) { } else if (dynamic) { - RLMRealmSetSchemaAndAlign(realm, [RLMSchema dynamicSchemaFromObjectStoreSchema:realm->_realm->schema()]); + realm->_schema = [RLMSchema dynamicSchemaFromObjectStoreSchema:realm->_realm->schema()]; + realm->_info = RLMSchemaInfo(realm); } else { // set/align schema or perform migration if needed @@ -344,7 +453,8 @@ + (instancetype)realmWithConfiguration:(RLMRealmConfiguration *)configuration er return nil; } - RLMRealmSetSchemaAndAlign(realm, schema); + realm->_schema = schema; + realm->_info = RLMSchemaInfo(realm); RLMRealmCreateAccessors(realm.schema); if (!readOnly) { @@ -353,39 +463,58 @@ + (instancetype)realmWithConfiguration:(RLMRealmConfiguration *)configuration er } } - if (config.cache) { + if (cache) { RLMCacheRealm(config.path, realm); } if (!readOnly) { realm->_realm->m_binding_context = RLMCreateBindingContext(realm); realm->_realm->m_binding_context->realm = realm->_realm; + + RLMAddSkipBackupAttributeToItemAtPath(config.path + ".management"); + RLMAddSkipBackupAttributeToItemAtPath(config.path + ".lock"); + RLMAddSkipBackupAttributeToItemAtPath(config.path + ".note"); } return RLMAutorelease(realm); } ++ (instancetype)uncachedSchemalessRealmWithConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error { + RLMRealm *realm = [[RLMRealm alloc] initPrivate]; + try { + realm->_realm = Realm::get_shared_realm(configuration.config); + } + catch (...) { + translateSharedGroupOpenException(configuration, error); + return nil; + } + return realm; +} + + (void)resetRealmState { RLMClearRealmCache(); realm::_impl::RealmCoordinator::clear_cache(); [RLMRealmConfiguration resetRealmConfigurationState]; } -- (void)verifyNotificationsAreSupported { +- (void)verifyNotificationsAreSupported:(bool)isCollection { [self verifyThread]; - if (_realm->config().read_only()) { + if (_realm->config().immutable()) { @throw RLMException(@"Read-only Realms do not change and do not have change notifications"); } if (!_realm->can_deliver_notifications()) { @throw RLMException(@"Can only add notification blocks from within runloops."); } + if (isCollection && _realm->is_in_transaction()) { + @throw RLMException(@"Cannot register notification blocks from within write transactions."); + } } - (RLMNotificationToken *)addNotificationBlock:(RLMNotificationBlock)block { if (!block) { @throw RLMException(@"The notification block should not be nil"); } - [self verifyNotificationsAreSupported]; + [self verifyNotificationsAreSupported:false]; _realm->read_group(); @@ -401,7 +530,7 @@ - (RLMNotificationToken *)addNotificationBlock:(RLMNotificationBlock)block { } - (void)sendNotifications:(RLMNotification)notification { - NSAssert(!_realm->config().read_only(), @"Read-only realms do not have notifications"); + NSAssert(!_realm->config().immutable(), @"Read-only realms do not have notifications"); if (_sendingNotifications) { return; } @@ -480,11 +609,11 @@ - (BOOL)commitWriteTransactionWithoutNotifying:(NSArray } } -- (void)transactionWithBlock:(void(^)(void))block { +- (void)transactionWithBlock:(__attribute__((noescape)) void(^)(void))block { [self transactionWithBlock:block error:nil]; } -- (BOOL)transactionWithBlock:(void(^)(void))block error:(NSError **)outError { +- (BOOL)transactionWithBlock:(__attribute__((noescape)) void(^)(void))block error:(NSError **)outError { [self beginWriteTransaction]; block(); if (_realm->is_in_transaction()) { @@ -586,9 +715,9 @@ - (void)addObject:(__unsafe_unretained RLMObject *const)object { RLMAddObjectToRealm(object, self, false); } -- (void)addObjects:(id)array { - for (RLMObject *obj in array) { - if (![obj isKindOfClass:[RLMObject class]]) { +- (void)addObjects:(id)objects { + for (RLMObject *obj in objects) { + if (![obj isKindOfClass:RLMObjectBase.class]) { @throw RLMException(@"Cannot insert objects of type %@ with addObjects:. Only RLMObjects are supported.", NSStringFromClass(obj.class)); } @@ -605,8 +734,13 @@ - (void)addOrUpdateObject:(RLMObject *)object { RLMAddObjectToRealm(object, self, true); } -- (void)addOrUpdateObjectsFromArray:(id)array { - for (RLMObject *obj in array) { +- (void)addOrUpdateObjects:(id)objects { + for (RLMObject *obj in objects) { + if (![obj isKindOfClass:RLMObjectBase.class]) { + @throw RLMException(@"Cannot add or update objects of type %@ with addOrUpdateObjects:. Only RLMObjects are" + " supported.", + NSStringFromClass(obj.class)); + } [self addOrUpdateObject:obj]; } } @@ -615,22 +749,28 @@ - (void)deleteObject:(RLMObject *)object { RLMDeleteObjectFromRealm(object, self); } -- (void)deleteObjects:(id)array { - if ([array respondsToSelector:@selector(realm)] && [array respondsToSelector:@selector(deleteObjectsFromRealm)]) { - if (self != (RLMRealm *)[array realm]) { +- (void)deleteObjects:(id)objects { + id idObjects = objects; + if ([idObjects respondsToSelector:@selector(realm)] + && [idObjects respondsToSelector:@selector(deleteObjectsFromRealm)]) { + if (self != (RLMRealm *)[idObjects realm]) { @throw RLMException(@"Can only delete objects from the Realm they belong to."); } - [array deleteObjectsFromRealm]; + [idObjects deleteObjectsFromRealm]; + return; } - else if ([array conformsToProtocol:@protocol(NSFastEnumeration)]) { - for (id obj in array) { - if ([obj isKindOfClass:RLMObjectBase.class]) { - RLMDeleteObjectFromRealm(obj, self); - } + if (auto array = RLMDynamicCast(objects)) { + if (array.type != RLMPropertyTypeObject) { + @throw RLMException(@"Cannot delete objects from RLMArray<%@>: only RLMObjects can be deleted.", + RLMTypeToString(array.type)); } } - else { - @throw RLMException(@"Invalid array type - container must be an RLMArray, RLMArray, or NSArray of RLMObjects"); + for (RLMObject *obj in objects) { + if (![obj isKindOfClass:RLMObjectBase.class]) { + @throw RLMException(@"Cannot delete objects of type %@ with deleteObjects:. Only RLMObjects can be deleted.", + NSStringFromClass(obj.class)); + } + RLMDeleteObjectFromRealm(obj, self); } } @@ -663,8 +803,8 @@ - (RLMObject *)objectWithClassName:(NSString *)className forPrimaryKey:(id)prima } + (uint64_t)schemaVersionAtURL:(NSURL *)fileURL encryptionKey:(NSData *)key error:(NSError **)error { + RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init]; try { - RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init]; config.fileURL = fileURL; config.encryptionKey = RLMRealmValidatedEncryptionKey(key); @@ -674,19 +814,12 @@ + (uint64_t)schemaVersionAtURL:(NSURL *)fileURL encryptionKey:(NSData *)key erro } return version; } - catch (std::exception &exp) { - RLMSetErrorOrThrow(RLMMakeError(RLMErrorFail, exp), error); + catch (...) { + translateSharedGroupOpenException(config, error); return RLMNotVersioned; } } -+ (nullable NSError *)migrateRealm:(RLMRealmConfiguration *)configuration { - // Preserves backwards compatibility - NSError *error; - [self performMigrationForConfiguration:configuration error:&error]; - return error; -} - + (BOOL)performMigrationForConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error { if (RLMGetAnyCachedRealmForPath(configuration.config.path)) { @throw RLMException(@"Cannot migrate Realms that are already open."); @@ -727,12 +860,55 @@ - (BOOL)writeCopyToURL:(NSURL *)fileURL encryptionKey:(NSData *)key error:(NSErr return NO; } +using Privilege = realm::ComputedPrivileges; +static bool hasPrivilege(realm::ComputedPrivileges actual, realm::ComputedPrivileges expected) { + return (static_cast(actual) & static_cast(expected)) == static_cast(expected); +} + +- (RLMRealmPrivileges)privilegesForRealm { + auto p = _realm->get_privileges(); + return { + .read = hasPrivilege(p, Privilege::Read), + .update = hasPrivilege(p, Privilege::Update), + .setPermissions = hasPrivilege(p, Privilege::SetPermissions), + .modifySchema = hasPrivilege(p, Privilege::ModifySchema), + }; +} + +- (RLMObjectPrivileges)privilegesForObject:(RLMObject *)object { + RLMVerifyAttached(object); + auto p = _realm->get_privileges(object->_row); + return { + .read = hasPrivilege(p, Privilege::Read), + .update = hasPrivilege(p, Privilege::Update), + .del = hasPrivilege(p, Privilege::Delete), + .setPermissions = hasPrivilege(p, Privilege::Delete), + }; +} + +- (RLMClassPrivileges)privilegesForClass:(Class)cls { + if (![cls respondsToSelector:@selector(_realmObjectName)]) { + @throw RLMException(@"Cannot get privileges for non-RLMObject class %@", cls); + } + return [self privilegesForClassNamed:[cls _realmObjectName] ?: [cls className]]; +} + +- (RLMClassPrivileges)privilegesForClassNamed:(NSString *)className { + auto p = _realm->get_privileges(className.UTF8String); + return { + .read = hasPrivilege(p, Privilege::Read), + .update = hasPrivilege(p, Privilege::Update), + .setPermissions = hasPrivilege(p, Privilege::SetPermissions), + .subscribe = hasPrivilege(p, Privilege::Query), + .create = hasPrivilege(p, Privilege::Create), + }; +} + - (void)registerEnumerator:(RLMFastEnumerator *)enumerator { if (!_collectionEnumerators) { _collectionEnumerators = [NSHashTable hashTableWithOptions:NSPointerFunctionsWeakMemory]; } [_collectionEnumerators addObject:enumerator]; - } - (void)unregisterEnumerator:(RLMFastEnumerator *)enumerator { diff --git a/Pods/Realm/Realm/RLMRealmConfiguration+Sync.mm b/Pods/Realm/Realm/RLMRealmConfiguration+Sync.mm index 528d732..36721d8 100644 --- a/Pods/Realm/Realm/RLMRealmConfiguration+Sync.mm +++ b/Pods/Realm/Realm/RLMRealmConfiguration+Sync.mm @@ -33,24 +33,33 @@ @implementation RLMRealmConfiguration (Sync) #pragma mark - API - (void)setSyncConfiguration:(RLMSyncConfiguration *)syncConfiguration { + if (self.config.should_compact_on_launch_function) { + @throw RLMException(@"Cannot set `syncConfiguration` when `shouldCompactOnLaunch` is set."); + } RLMSyncUser *user = syncConfiguration.user; if (user.state == RLMSyncUserStateError) { @throw RLMException(@"Cannot set a sync configuration which has an errored-out user."); } - NSURL *realmURL = syncConfiguration.realmURL; // Ensure sync manager is initialized, if it hasn't already been. [RLMSyncManager sharedManager]; NSAssert(user.identity, @"Cannot call this method on a user that doesn't have an identity."); + self.config.in_memory = false; + self.config.sync_config = std::make_shared([syncConfiguration rawConfiguration]); + self.config.schema_mode = realm::SchemaMode::Additive; + if (syncConfiguration.customFileURL) { self.config.path = syncConfiguration.customFileURL.path.UTF8String; } else { - self.config.path = SyncManager::shared().path_for_realm([user.identity UTF8String], - [realmURL.absoluteString UTF8String]); + self.config.path = SyncManager::shared().path_for_realm(*[user _syncUser], + self.config.sync_config->realm_url()); + } + + if (!self.config.encryption_key.empty()) { + auto& sync_encryption_key = self.config.sync_config->realm_encryption_key; + sync_encryption_key = std::array(); + std::copy_n(self.config.encryption_key.begin(), 64, sync_encryption_key->begin()); } - self.config.in_memory = false; - self.config.sync_config = std::make_shared([syncConfiguration rawConfiguration]); - self.config.schema_mode = realm::SchemaMode::Additive; } - (RLMSyncConfiguration *)syncConfiguration { diff --git a/Pods/Realm/Realm/RLMRealmConfiguration.mm b/Pods/Realm/Realm/RLMRealmConfiguration.mm index b6ea49e..3635f81 100644 --- a/Pods/Realm/Realm/RLMRealmConfiguration.mm +++ b/Pods/Realm/Realm/RLMRealmConfiguration.mm @@ -25,6 +25,7 @@ #import "schema.hpp" #import "shared_realm.hpp" +#import "sync/sync_config.hpp" static NSString *const c_RLMRealmConfigurationProperties[] = { @"fileURL", @@ -34,6 +35,7 @@ @"schemaVersion", @"migrationBlock", @"deleteRealmIfMigrationNeeded", + @"shouldCompactOnLaunch", @"dynamic", @"customSchema", }; @@ -73,12 +75,14 @@ + (void)setDefaultConfiguration:(RLMRealmConfiguration *)configuration { } + (RLMRealmConfiguration *)rawDefaultConfiguration { + RLMRealmConfiguration *configuration; @synchronized(c_defaultRealmFileName) { if (!s_defaultConfiguration) { s_defaultConfiguration = [[RLMRealmConfiguration alloc] init]; } + configuration = s_defaultConfiguration; } - return s_defaultConfiguration; + return configuration; } + (void)resetRealmConfigurationState { @@ -93,6 +97,14 @@ - (instancetype)init { static NSURL *defaultRealmURL = [NSURL fileURLWithPath:RLMRealmPathForFile(c_defaultRealmFileName)]; self.fileURL = defaultRealmURL; self.schemaVersion = 0; + self.cache = YES; + + // We have our own caching of RLMRealm instances, so the ObjectStore + // cache is at best pointless, and may result in broken behavior when + // a realm::Realm instance outlives the RLMRealm (due to collection + // notifiers being in the middle of running when the RLMRealm is + // dealloced) and then reused for a new RLMRealm + _config.cache = false; } return self; @@ -101,8 +113,10 @@ - (instancetype)init { - (instancetype)copyWithZone:(NSZone *)zone { RLMRealmConfiguration *configuration = [[[self class] allocWithZone:zone] init]; configuration->_config = _config; + configuration->_cache = _cache; configuration->_dynamic = _dynamic; configuration->_migrationBlock = _migrationBlock; + configuration->_shouldCompactOnLaunch = _shouldCompactOnLaunch; configuration->_customSchema = _customSchema; return configuration; } @@ -176,22 +190,31 @@ - (void)setEncryptionKey:(NSData * __nullable)encryptionKey { if (NSData *key = RLMRealmValidatedEncryptionKey(encryptionKey)) { auto bytes = static_cast(key.bytes); _config.encryption_key.assign(bytes, bytes + key.length); + if (_config.sync_config) { + auto& sync_encryption_key = self.config.sync_config->realm_encryption_key; + sync_encryption_key = std::array(); + std::copy_n(_config.encryption_key.begin(), 64, sync_encryption_key->begin()); + } } else { _config.encryption_key.clear(); + if (_config.sync_config) + _config.sync_config->realm_encryption_key = realm::util::none; } } - (BOOL)readOnly { - return _config.read_only(); + return _config.immutable(); } - (void)setReadOnly:(BOOL)readOnly { if (readOnly) { if (self.deleteRealmIfMigrationNeeded) { @throw RLMException(@"Cannot set `readOnly` when `deleteRealmIfMigrationNeeded` is set."); + } else if (self.shouldCompactOnLaunch) { + @throw RLMException(@"Cannot set `readOnly` when `shouldCompactOnLaunch` is set."); } - _config.schema_mode = realm::SchemaMode::ReadOnly; + _config.schema_mode = realm::SchemaMode::Immutable; } else if (self.readOnly) { _config.schema_mode = realm::SchemaMode::Automatic; @@ -235,15 +258,7 @@ - (void)setObjectClasses:(NSArray *)objectClasses { - (void)setDynamic:(bool)dynamic { _dynamic = dynamic; - _config.cache = !dynamic; -} - -- (bool)cache { - return _config.cache; -} - -- (void)setCache:(bool)cache { - _config.cache = cache; + self.cache = !dynamic; } - (bool)disableFormatUpgrade { @@ -262,5 +277,27 @@ - (void)setSchemaMode:(realm::SchemaMode)mode { _config.schema_mode = mode; } -@end +- (NSString *)pathOnDisk { + return @(_config.path.c_str()); +} + +- (void)setShouldCompactOnLaunch:(RLMShouldCompactOnLaunchBlock)shouldCompactOnLaunch { + if (shouldCompactOnLaunch) { + if (self.readOnly) { + @throw RLMException(@"Cannot set `shouldCompactOnLaunch` when `readOnly` is set."); + } + _config.should_compact_on_launch_function = [=](size_t totalBytes, size_t usedBytes) { + return shouldCompactOnLaunch(totalBytes, usedBytes); + }; + } + else { + _config.should_compact_on_launch_function = nullptr; + } + _shouldCompactOnLaunch = shouldCompactOnLaunch; +} + +- (void)setCustomSchemaWithoutCopying:(RLMSchema *)schema { + _customSchema = schema; +} +@end diff --git a/Pods/Realm/Realm/RLMRealmUtil.mm b/Pods/Realm/Realm/RLMRealmUtil.mm index 0d38a91..10d6ff3 100644 --- a/Pods/Realm/Realm/RLMRealmUtil.mm +++ b/Pods/Realm/Realm/RLMRealmUtil.mm @@ -36,10 +36,10 @@ #import // Global realm state -static std::mutex s_realmCacheMutex; -static std::map s_realmsPerPath; +static std::mutex& s_realmCacheMutex = *new std::mutex(); +static std::map& s_realmsPerPath = *new std::map(); -void RLMCacheRealm(std::string const& path, RLMRealm *realm) { +void RLMCacheRealm(std::string const& path, __unsafe_unretained RLMRealm *const realm) { std::lock_guard lock(s_realmCacheMutex); NSMapTable *realms = s_realmsPerPath[path]; if (!realms) { @@ -64,25 +64,29 @@ void RLMClearRealmCache() { s_realmsPerPath.clear(); } +bool RLMIsInRunLoop() { + // The main thread may not be in a run loop yet if we're called from + // something like `applicationDidFinishLaunching:`, but it presumably will + // be in the future + if ([NSThread isMainThread]) { + return true; + } + // Current mode indicates why the current callout from the runloop was made, + // and is null if a runloop callout isn't currently being processed + if (auto mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent())) { + CFRelease(mode); + return true; + } + return false; +} + namespace { class RLMNotificationHelper : public realm::BindingContext { public: RLMNotificationHelper(RLMRealm *realm) : _realm(realm) { } bool can_deliver_notifications() const noexcept override { - // The main thread may not be in a run loop yet if we're called from - // something like `applicationDidFinishLaunching:`, but it presumably will - // be in the future - if ([NSThread isMainThread]) { - return true; - } - // Current mode indicates why the current callout from the runloop was made, - // and is null if a runloop callout isn't currently being processed - if (auto mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent())) { - CFRelease(mode); - return true; - } - return false; + return RLMIsInRunLoop(); } void changes_available() override { @@ -138,6 +142,6 @@ void did_change(std::vector const& observed, std::vector c } // anonymous namespace -std::unique_ptr RLMCreateBindingContext(RLMRealm *realm) { +std::unique_ptr RLMCreateBindingContext(__unsafe_unretained RLMRealm *const realm) { return std::unique_ptr(new RLMNotificationHelper(realm)); } diff --git a/Pods/Realm/Realm/RLMResults.mm b/Pods/Realm/Realm/RLMResults.mm index a2d1f30..956393c 100644 --- a/Pods/Realm/Realm/RLMResults.mm +++ b/Pods/Realm/Realm/RLMResults.mm @@ -16,8 +16,9 @@ // //////////////////////////////////////////////////////////////////////////// -#import "RLMResults_Private.h" +#import "RLMResults_Private.hpp" +#import "RLMAccessor.hpp" #import "RLMArray_Private.hpp" #import "RLMCollection_Private.hpp" #import "RLMObjectSchema_Private.hpp" @@ -32,8 +33,8 @@ #import "RLMUtil.hpp" #import "results.hpp" +#import "shared_realm.hpp" -#import #import #import @@ -52,7 +53,6 @@ @interface RLMResults () // RLMResults implementation // @implementation RLMResults { - realm::Results _results; RLMRealm *_realm; RLMClassInfo *_info; } @@ -62,53 +62,52 @@ - (instancetype)initPrivate { return self; } +- (instancetype)initWithResults:(Results)results { + if (self = [super init]) { + _results = std::move(results); + } + return self; +} + static void assertKeyPathIsNotNested(NSString *keyPath) { if ([keyPath rangeOfString:@"."].location != NSNotFound) { @throw RLMException(@"Nested key paths are not supported yet for KVC collection operators."); } } -[[gnu::noinline]] -[[noreturn]] -static void throwError(NSString *aggregateMethod) { +void RLMThrowResultsError(NSString *aggregateMethod) { try { throw; } catch (realm::InvalidTransactionException const&) { - @throw RLMException(@"Cannot modify Results outside of a write transaction"); + @throw RLMException(@"Cannot modify Results outside of a write transaction."); } catch (realm::IncorrectThreadException const&) { - @throw RLMException(@"Realm accessed from incorrect thread"); + @throw RLMException(@"Realm accessed from incorrect thread."); } catch (realm::Results::InvalidatedException const&) { - @throw RLMException(@"RLMResults has been invalidated"); + @throw RLMException(@"RLMResults has been invalidated."); } catch (realm::Results::DetatchedAccessorException const&) { - @throw RLMException(@"Object has been invalidated"); + @throw RLMException(@"Object has been invalidated."); } catch (realm::Results::IncorrectTableException const& e) { - @throw RLMException(@"Object type '%s' does not match RLMResults type '%s'.", + @throw RLMException(@"Object of type '%s' does not match RLMResults type '%s'.", e.actual.data(), e.expected.data()); } catch (realm::Results::OutOfBoundsIndexException const& e) { - @throw RLMException(@"Index %zu is out of bounds (must be less than %zu)", + @throw RLMException(@"Index %zu is out of bounds (must be less than %zu).", e.requested, e.valid_count); } catch (realm::Results::UnsupportedColumnTypeException const& e) { - @throw RLMException(@"%@ is not supported for %@ property '%s'", + @throw RLMException(@"%@ is not supported for %s%s property '%s'.", aggregateMethod, - RLMTypeToString((RLMPropertyType)e.column_type), + string_for_property_type(e.property_type), + is_nullable(e.property_type) ? "?" : "", e.column_name.data()); } -} - -template -static auto translateErrors(Function&& f, NSString *aggregateMethod=nil) { - try { - return f(); - } - catch (...) { - throwError(aggregateMethod); + catch (std::exception const& e) { + @throw RLMException(e); } } @@ -131,15 +130,32 @@ static inline void RLMResultsValidateInWriteTransaction(__unsafe_unretained RLMR } - (BOOL)isInvalidated { - return translateErrors([&] { return !_results.is_valid(); }); + return translateRLMResultsErrors([&] { return !_results.is_valid(); }); } - (NSUInteger)count { - return translateErrors([&] { return _results.size(); }); + return translateRLMResultsErrors([&] { return _results.size(); }); +} + +- (RLMPropertyType)type { + return translateRLMResultsErrors([&] { + return static_cast(_results.get_type() & ~realm::PropertyType::Nullable); + }); +} + +- (BOOL)isOptional { + return translateRLMResultsErrors([&] { + return is_nullable(_results.get_type()); + }); } - (NSString *)objectClassName { - return RLMStringDataToNSString(_results.get_object_type()); + return translateRLMResultsErrors([&] { + if (_info && _results.get_type() == realm::PropertyType::Object) { + return _info->rlmObjectSchema.className; + } + return (NSString *)nil; + }); } - (RLMClassInfo *)objectInfo { @@ -152,18 +168,7 @@ - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state if (!_info) { return 0; } - - __autoreleasing RLMFastEnumerator *enumerator; - if (state->state == 0) { - enumerator = [[RLMFastEnumerator alloc] initWithCollection:self objectSchema:*_info]; - state->extra[0] = (long)enumerator; - state->extra[1] = self.count; - } - else { - enumerator = (__bridge id)(void *)state->extra[0]; - } - - return [enumerator countByEnumeratingWithState:state count:len]; + return RLMFastEnumerate(state, len, self); } - (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat, ... { @@ -184,106 +189,91 @@ - (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate { return NSNotFound; } - Query query = translateErrors([&] { return _results.get_query(); }); - query.and_query(RLMPredicateToQuery(predicate, _info->rlmObjectSchema, _realm.schema, _realm.group)); - query.sync_view_if_needed(); - -#if REALM_VER_MAJOR >= 2 - size_t indexInTable; - if (const auto& sort = _results.get_sort()) { - // A sort order is specified so we need to return the first match given that ordering. - TableView table_view = query.find_all(); - table_view.sort(sort); - if (!table_view.size()) { - return NSNotFound; + return translateRLMResultsErrors([&] { + if (_results.get_type() != realm::PropertyType::Object) { + @throw RLMException(@"Querying is currently only implemented for arrays of Realm Objects"); } - indexInTable = table_view.get_source_ndx(0); - } else { - indexInTable = query.find(); - } - if (indexInTable == realm::not_found) { - return NSNotFound; - } - return RLMConvertNotFound(_results.index_of(indexInTable)); -#else - TableView table_view; - if (const auto& sort = _results.get_sort()) { - // A sort order is specified so we need to return the first match given that ordering. - table_view = query.find_all(); - table_view.sort(sort); - } else { - table_view = query.find_all(0, -1, 1); - } - if (!table_view.size()) { - return NSNotFound; - } - return _results.index_of(table_view.get_source_ndx(0)); -#endif + return RLMConvertNotFound(_results.index_of(RLMPredicateToQuery(predicate, _info->rlmObjectSchema, _realm.schema, _realm.group))); + }); } - (id)objectAtIndex:(NSUInteger)index { - return translateErrors([&] { - return RLMCreateObjectAccessor(_realm, *_info, _results.get(index)); + RLMAccessorContext ctx(_realm, *_info); + return translateRLMResultsErrors([&] { + return _results.get(ctx, index); }); } - (id)firstObject { - auto row = translateErrors([&] { return _results.first(); }); - return row ? RLMCreateObjectAccessor(_realm, *_info, *row) : nil; + if (!_info) { + return nil; + } + RLMAccessorContext ctx(_realm, *_info); + return translateRLMResultsErrors([&] { + return _results.first(ctx); + }); } - (id)lastObject { - auto row = translateErrors([&] { return _results.last(); }); - return row ? RLMCreateObjectAccessor(_realm, *_info, *row) : nil; + if (!_info) { + return nil; + } + RLMAccessorContext ctx(_realm, *_info); + return translateRLMResultsErrors([&] { + return _results.last(ctx); + }); } - (NSUInteger)indexOfObject:(RLMObject *)object { - if (!object || (!object->_realm && !object.invalidated)) { + if (!_info || !object || (!object->_realm && !object.invalidated)) { return NSNotFound; } - - return translateErrors([&] { - return RLMConvertNotFound(_results.index_of(object->_row)); + RLMAccessorContext ctx(_realm, *_info); + return translateRLMResultsErrors([&] { + return RLMConvertNotFound(_results.index_of(ctx, object)); }); } - (id)valueForKeyPath:(NSString *)keyPath { - if ([keyPath characterAtIndex:0] == '@') { - if ([keyPath isEqualToString:@"@count"]) { - return @(self.count); - } - NSRange operatorRange = [keyPath rangeOfString:@"." options:NSLiteralSearch]; - NSUInteger keyPathLength = keyPath.length; - NSUInteger separatorIndex = operatorRange.location != NSNotFound ? operatorRange.location : keyPathLength; - NSString *operatorName = [keyPath substringWithRange:NSMakeRange(1, separatorIndex - 1)]; - SEL opSelector = NSSelectorFromString([NSString stringWithFormat:@"_%@ForKeyPath:", operatorName]); - BOOL isValidOperator = [self respondsToSelector:opSelector]; - if (!isValidOperator) { - @throw RLMException(@"Unsupported KVC collection operator found in key path '%@'", keyPath); - } - else if (separatorIndex >= keyPathLength - 1) { - @throw RLMException(@"Missing key path for KVC collection operator %@ in key path '%@'", operatorName, keyPath); - } - NSString *operatorKeyPath = [keyPath substringFromIndex:separatorIndex + 1]; - if (isValidOperator) { - return ((id(*)(id, SEL, id))objc_msgSend)(self, opSelector, operatorKeyPath); - } + if ([keyPath characterAtIndex:0] != '@') { + return [super valueForKeyPath:keyPath]; + } + if ([keyPath isEqualToString:@"@count"]) { + return @(self.count); } - return [super valueForKeyPath:keyPath]; + + NSRange operatorRange = [keyPath rangeOfString:@"." options:NSLiteralSearch]; + NSUInteger keyPathLength = keyPath.length; + NSUInteger separatorIndex = operatorRange.location != NSNotFound ? operatorRange.location : keyPathLength; + NSString *operatorName = [keyPath substringWithRange:NSMakeRange(1, separatorIndex - 1)]; + SEL opSelector = NSSelectorFromString([NSString stringWithFormat:@"_%@ForKeyPath:", operatorName]); + if (![self respondsToSelector:opSelector]) { + @throw RLMException(@"Unsupported KVC collection operator found in key path '%@'", keyPath); + } + if (separatorIndex >= keyPathLength - 1) { + @throw RLMException(@"Missing key path for KVC collection operator %@ in key path '%@'", + operatorName, keyPath); + } + NSString *operatorKeyPath = [keyPath substringFromIndex:separatorIndex + 1]; + return ((id(*)(id, SEL, id))objc_msgSend)(self, opSelector, operatorKeyPath); } - (id)valueForKey:(NSString *)key { - return translateErrors([&] { - return RLMCollectionValueForKey(self, key); + if (!_info) { + return @[]; + } + return translateRLMResultsErrors([&] { + return RLMCollectionValueForKey(_results, key, _realm, *_info); }); } - (void)setValue:(id)value forKey:(NSString *)key { - translateErrors([&] { RLMResultsValidateInWriteTransaction(self); }); + translateRLMResultsErrors([&] { RLMResultsValidateInWriteTransaction(self); }); RLMCollectionSetValueForKey(self, key, value); } -- (NSNumber *)_aggregateForKeyPath:(NSString *)keyPath method:(util::Optional (Results::*)(size_t))method +- (NSNumber *)_aggregateForKeyPath:(NSString *)keyPath + method:(util::Optional (Results::*)(size_t))method methodName:(NSString *)methodName returnNilForEmpty:(BOOL)returnNilForEmpty { assertKeyPathIsNotNested(keyPath); return [self aggregate:keyPath method:method methodName:methodName returnNilForEmpty:returnNilForEmpty]; @@ -302,13 +292,14 @@ - (NSNumber *)_sumForKeyPath:(NSString *)keyPath { } - (NSNumber *)_avgForKeyPath:(NSString *)keyPath { - return [self _aggregateForKeyPath:keyPath method:&Results::average methodName:@"@avg" returnNilForEmpty:YES]; + assertKeyPathIsNotNested(keyPath); + return [self averageOfProperty:keyPath]; } - (NSArray *)_unionOfObjectsForKeyPath:(NSString *)keyPath { assertKeyPathIsNotNested(keyPath); - return translateErrors([&] { - return RLMCollectionValueForKey(self, keyPath); + return translateRLMResultsErrors([&] { + return RLMCollectionValueForKey(_results, keyPath, _realm, *_info); }); } @@ -322,12 +313,12 @@ - (NSArray *)_unionOfArraysForKeyPath:(NSString *)keyPath { @throw RLMException(@"self is not a valid key-path for a KVC array collection operator as 'unionOfArrays'."); } - return translateErrors([&] { - NSArray *nestedResults = RLMCollectionValueForKey(self, keyPath); - NSMutableArray *flatArray = [NSMutableArray arrayWithCapacity:nestedResults.count]; - for (id array in nestedResults) { - NSArray *nsArray = RLMCollectionValueForKey(array, @"self"); - [flatArray addObjectsFromArray:nsArray]; + return translateRLMResultsErrors([&] { + NSMutableArray *flatArray = [NSMutableArray new]; + for (id array in RLMCollectionValueForKey(_results, keyPath, _realm, *_info)) { + for (id value in array) { + [flatArray addObject:value]; + } } return flatArray; }); @@ -350,10 +341,13 @@ - (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args { } - (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate { - return translateErrors([&] { + return translateRLMResultsErrors([&] { if (_results.get_mode() == Results::Mode::Empty) { return self; } + if (_results.get_type() != realm::PropertyType::Object) { + @throw RLMException(@"Querying is currently only implemented for arrays of Realm Objects"); + } auto query = RLMPredicateToQuery(predicate, _info->rlmObjectSchema, _realm.schema, _realm.group); return [RLMResults resultsWithObjectInfo:*_info results:_results.filter(std::move(query))]; }); @@ -363,20 +357,37 @@ - (RLMResults *)sortedResultsUsingKeyPath:(NSString *)keyPath ascending:(BOOL)as return [self sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:keyPath ascending:ascending]]]; } -- (RLMResults *)sortedResultsUsingProperty:(NSString *)property ascending:(BOOL)ascending { - return [self sortedResultsUsingKeyPath:property ascending:ascending]; -} - - (RLMResults *)sortedResultsUsingDescriptors:(NSArray *)properties { if (properties.count == 0) { return self; } - return translateErrors([&] { + return translateRLMResultsErrors([&] { if (_results.get_mode() == Results::Mode::Empty) { return self; } + return [RLMResults resultsWithObjectInfo:*_info + results:_results.sort(RLMSortDescriptorsToKeypathArray(properties))]; + }); +} - return [RLMResults resultsWithObjectInfo:*_info results:_results.sort(RLMSortDescriptorFromDescriptors(*_info, properties))]; +- (RLMResults *)distinctResultsUsingKeyPaths:(NSArray *)keyPaths { + for (NSString *keyPath in keyPaths) { + if ([keyPath rangeOfString:@"@"].location != NSNotFound) { + @throw RLMException(@"Cannot distinct on keypath '%@': KVC collection operators are not supported.", keyPath); + } + } + + return translateRLMResultsErrors([&] { + if (_results.get_mode() == Results::Mode::Empty) { + return self; + } + + std::vector keyPathsVector; + for (NSString *keyPath in keyPaths) { + keyPathsVector.push_back(keyPath.UTF8String); + } + + return [RLMResults resultsWithObjectInfo:*_info results:_results.distinct(keyPathsVector)]; }); } @@ -389,32 +400,48 @@ - (id)aggregate:(NSString *)property method:(util::Optional (Results::*)( if (_results.get_mode() == Results::Mode::Empty) { return returnNilForEmpty ? nil : @0; } - size_t column = _info->tableColumn(property); - auto value = translateErrors([&] { return (_results.*method)(column); }, methodName); - if (!value) { - return nil; + size_t column = 0; + if (self.type == RLMPropertyTypeObject || ![property isEqualToString:@"self"]) { + column = _info->tableColumn(property); } - return RLMMixedToObjc(*value); + + auto value = translateRLMResultsErrors([&] { return (_results.*method)(column); }, methodName); + return value ? RLMMixedToObjc(*value) : nil; } - (id)minOfProperty:(NSString *)property { - return [self aggregate:property method:&Results::min methodName:@"minOfProperty" returnNilForEmpty:YES]; + return [self aggregate:property method:&Results::min + methodName:@"minOfProperty" returnNilForEmpty:YES]; } - (id)maxOfProperty:(NSString *)property { - return [self aggregate:property method:&Results::max methodName:@"maxOfProperty" returnNilForEmpty:YES]; + return [self aggregate:property method:&Results::max + methodName:@"maxOfProperty" returnNilForEmpty:YES]; } - (id)sumOfProperty:(NSString *)property { - return [self aggregate:property method:&Results::sum methodName:@"sumOfProperty" returnNilForEmpty:NO]; + return [self aggregate:property method:&Results::sum + methodName:@"sumOfProperty" returnNilForEmpty:NO]; } - (id)averageOfProperty:(NSString *)property { - return [self aggregate:property method:&Results::average methodName:@"averageOfProperty" returnNilForEmpty:YES]; + if (_results.get_mode() == Results::Mode::Empty) { + return nil; + } + size_t column = 0; + if (self.type == RLMPropertyTypeObject || ![property isEqualToString:@"self"]) { + column = _info->tableColumn(property); + } + auto value = translateRLMResultsErrors([&] { return _results.average(column); }, @"averageOfProperty"); + return value ? @(*value) : nil; } - (void)deleteObjectsFromRealm { - return translateErrors([&] { + if (self.type != RLMPropertyTypeObject) { + @throw RLMException(@"Cannot delete objects from RLMResults<%@>: only RLMObjects can be deleted.", + RLMTypeToString(self.type)); + } + return translateRLMResultsErrors([&] { if (_results.get_mode() == Results::Mode::Table) { RLMResultsValidateInWriteTransaction(self); RLMClearTable(*_info); @@ -429,12 +456,15 @@ - (NSString *)description { return RLMDescriptionWithMaxDepth(@"RLMResults", self, RLMDescriptionMaxDepth); } -- (NSUInteger)indexInSource:(NSUInteger)index { - return translateErrors([&] { return _results.get(index).get_index(); }); +- (realm::TableView)tableView { + return translateRLMResultsErrors([&] { return _results.get_tableview(); }); } -- (realm::TableView)tableView { - return translateErrors([&] { return _results.get_tableview(); }); +- (RLMFastEnumerator *)fastEnumerator { + return translateRLMResultsErrors([&] { + return [[RLMFastEnumerator alloc] initWithResults:_results collection:self + realm:_realm classInfo:*_info]; + }); } // The compiler complains about the method's argument type not matching due to @@ -444,7 +474,7 @@ - (NSUInteger)indexInSource:(NSUInteger)index { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmismatched-parameter-types" - (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMResults *, RLMCollectionChange *, NSError *))block { - [_realm verifyNotificationsAreSupported]; + [_realm verifyNotificationsAreSupported:true]; return RLMAddNotificationBlock(self, _results, block, true); } #pragma clang diagnostic pop @@ -479,4 +509,8 @@ + (instancetype)objectWithThreadSafeReference:(std::unique_ptr_objectSchemaByName enumerateKeysAndObjectsUsingBlock:^(id, RLMObjectSchema *objectSchema, BOOL *) { for (RLMProperty *prop in objectSchema.properties) { - if (prop.type != RLMPropertyTypeObject && prop.type != RLMPropertyTypeArray) { + if (prop.type != RLMPropertyTypeObject) { continue; } if (!schema->_objectSchemaByName[prop.objectClassName]) { @@ -207,7 +222,9 @@ + (RLMObjectSchema *)sharedSchemaForClass:(Class)cls { } RLMRegisterClassLocalNames(&cls, 1); - return RLMRegisterClass(cls); + RLMObjectSchema *objectSchema = RLMRegisterClass(cls); + [cls initializeLinkedObjectSchemas]; + return objectSchema; } } @@ -215,6 +232,10 @@ + (instancetype)partialSharedSchema { return s_sharedSchema; } ++ (instancetype)partialPrivateSharedSchema { + return s_privateSharedSchema; +} + // schema based on runtime objects + (instancetype)sharedSchema { @synchronized(s_localNameToClass) { @@ -331,8 +352,21 @@ - (Schema)objectStoreCopy { std::vector schema; schema.reserve(_objectSchemaByName.count); [_objectSchemaByName enumerateKeysAndObjectsUsingBlock:[&](NSString *, RLMObjectSchema *objectSchema, BOOL *) { - schema.push_back(objectSchema.objectStoreCopy); + schema.push_back([objectSchema objectStoreCopy:self]); }]; + + // Having both obj-c and Swift classes for the same tables results in + // duplicate ObjectSchemas that we need to filter out + std::sort(begin(schema), end(schema), [](auto&& a, auto&& b) { return a.name < b.name; }); + schema.erase(std::unique(begin(schema), end(schema), [](auto&& a, auto&& b) { + if (a.name == b.name) { + // If we make _realmObjectName public this needs to be turned into an exception + REALM_ASSERT_DEBUG(a.persisted_properties == b.persisted_properties); + return true; + } + return false; + }), end(schema)); + _objectStoreSchema = std::move(schema); } return _objectStoreSchema; diff --git a/Pods/Realm/Realm/RLMSyncConfiguration.mm b/Pods/Realm/Realm/RLMSyncConfiguration.mm index 8625f5e..bebd0bb 100644 --- a/Pods/Realm/Realm/RLMSyncConfiguration.mm +++ b/Pods/Realm/Realm/RLMSyncConfiguration.mm @@ -18,8 +18,10 @@ #import "RLMSyncConfiguration_Private.hpp" +#import "RLMRealmConfiguration+Sync.h" #import "RLMSyncManager_Private.h" #import "RLMSyncSession_Private.hpp" +#import "RLMSyncSessionRefreshHandle.hpp" #import "RLMSyncUser_Private.hpp" #import "RLMSyncUtil_Private.hpp" #import "RLMUtil.hpp" @@ -37,6 +39,8 @@ RLMSyncSystemErrorKind errorKindForSyncError(SyncError error) { if (error.is_client_reset_requested()) { return RLMSyncSystemErrorKindClientReset; + } else if (error.error_code == ProtocolError::permission_denied) { + return RLMSyncSystemErrorKindPermissionDenied; } else if (error.error_code == ProtocolError::bad_authentication) { return RLMSyncSystemErrorKindUser; } else if (error.is_session_level_protocol_error()) { @@ -66,6 +70,7 @@ @interface RLMSyncConfiguration () { - (instancetype)initWithUser:(RLMSyncUser *)user realmURL:(NSURL *)url customFileURL:(nullable NSURL *)customFileURL + isPartial:(BOOL)isPartial stopPolicy:(RLMSyncStopPolicy)stopPolicy errorHandler:(std::function)errorHandler; @end @@ -76,7 +81,7 @@ @implementation RLMSyncConfiguration - (instancetype)initWithRawConfig:(realm::SyncConfig)config { if (self = [super init]) { - _config = std::make_unique(config); + _config = std::make_unique(std::move(config)); } return self; } @@ -88,7 +93,32 @@ - (BOOL)isEqual:(id)object { RLMSyncConfiguration *that = (RLMSyncConfiguration *)object; return [self.realmURL isEqual:that.realmURL] && [self.user isEqual:that.user] - && self.stopPolicy == that.stopPolicy; + && self.stopPolicy == that.stopPolicy + && self.fullSynchronization == that.fullSynchronization; +} + +- (void)setEnableSSLValidation:(BOOL)enableSSLValidation { + _config->client_validate_ssl = (bool)enableSSLValidation; +} + +- (BOOL)enableSSLValidation { + return (BOOL)_config->client_validate_ssl; +} + +- (void)setIsPartial:(BOOL)isPartial { + _config->is_partial = (bool)isPartial; +} + +- (BOOL)isPartial { + return (BOOL)_config->is_partial; +} + +- (void)setFullSynchronization:(BOOL)fullSynchronization { + _config->is_partial = !(bool)fullSynchronization; +} + +- (BOOL)fullSynchronization { + return !(BOOL)_config->is_partial; } - (realm::SyncConfig)rawConfiguration { @@ -107,8 +137,23 @@ - (void)setStopPolicy:(RLMSyncStopPolicy)stopPolicy { _config->stop_policy = translateStopPolicy(stopPolicy); } +- (NSString *)urlPrefix { + if (_config->url_prefix) { + return @(_config->url_prefix->c_str()); + } + return nil; +} + +- (void)setUrlPrefix:(NSString *)urlPrefix { + if (urlPrefix) { + _config->url_prefix.emplace(urlPrefix.UTF8String); + } else { + _config->url_prefix = none; + } +} + - (NSURL *)realmURL { - NSString *rawStringURL = @(_config->realm_url.c_str()); + NSString *rawStringURL = @(_config->reference_realm_url.c_str()); return [NSURL URLWithString:rawStringURL]; } @@ -116,13 +161,32 @@ - (instancetype)initWithUser:(RLMSyncUser *)user realmURL:(NSURL *)url { return [self initWithUser:user realmURL:url customFileURL:nil + isPartial:NO stopPolicy:RLMSyncStopPolicyAfterChangesUploaded errorHandler:nullptr]; } +- (instancetype)initWithUser:(RLMSyncUser *)user + realmURL:(NSURL *)url + isPartial:(BOOL)isPartial + urlPrefix:(NSString *)urlPrefix + stopPolicy:(RLMSyncStopPolicy)stopPolicy + enableSSLValidation:(BOOL)enableSSLValidation { + auto config = [self initWithUser:user + realmURL:url + customFileURL:nil + isPartial:isPartial + stopPolicy:stopPolicy + errorHandler:nullptr]; + config.urlPrefix = urlPrefix; + config.enableSSLValidation = enableSSLValidation; + return config; +} + - (instancetype)initWithUser:(RLMSyncUser *)user realmURL:(NSURL *)url customFileURL:(nullable NSURL *)customFileURL + isPartial:(BOOL)isPartial stopPolicy:(RLMSyncStopPolicy)stopPolicy errorHandler:(std::function)errorHandler { if (self = [super init]) { @@ -132,9 +196,15 @@ - (instancetype)initWithUser:(RLMSyncUser *)user auto bindHandler = [=](auto&, const SyncConfig& config, const std::shared_ptr& session) { - [user _bindSessionWithConfig:config - session:session - completion:[RLMSyncManager sharedManager].sessionCompletionNotifier]; + const std::shared_ptr& user = config.user; + NSURL *realmURL = [NSURL URLWithString:@(config.realm_url().c_str())]; + NSString *path = [realmURL path]; + REALM_ASSERT(realmURL && path); + RLMSyncSessionRefreshHandle *handle = [[RLMSyncSessionRefreshHandle alloc] initWithRealmURL:realmURL + user:user + session:std::move(session) + completionBlock:[RLMSyncManager sharedManager].sessionCompletionNotifier]; + context_for(user).register_refresh_handle([path UTF8String], handle); }; if (!errorHandler) { errorHandler = [=](std::shared_ptr errored_session, @@ -156,15 +226,45 @@ - (instancetype)initWithUser:(RLMSyncUser *)user _config = std::make_unique(SyncConfig{ [user _syncUser], - [[url absoluteString] UTF8String], - translateStopPolicy(stopPolicy), - std::move(bindHandler), - std::move(errorHandler) + [[url absoluteString] UTF8String] }); + _config->stop_policy = translateStopPolicy(stopPolicy); + _config->bind_session_handler = std::move(bindHandler); + _config->error_handler = std::move(errorHandler); + _config->is_partial = isPartial; + + if (NSString *authorizationHeaderName = [RLMSyncManager sharedManager].authorizationHeaderName) { + _config->authorization_header_name.emplace(authorizationHeaderName.UTF8String); + } + if (NSDictionary *customRequestHeaders = [RLMSyncManager sharedManager].customRequestHeaders) { + for (NSString *key in customRequestHeaders) { + _config->custom_http_headers.emplace(key.UTF8String, customRequestHeaders[key].UTF8String); + } + } + self.customFileURL = customFileURL; return self; } return nil; } ++ (RLMRealmConfiguration *)automaticConfiguration { + if (RLMSyncUser.allUsers.count != 1) + @throw RLMException(@"The automatic configuration requires there be exactly one logged-in sync user."); + + return [RLMSyncConfiguration automaticConfigurationForUser:RLMSyncUser.currentUser]; +} + ++ (RLMRealmConfiguration *)automaticConfigurationForUser:(RLMSyncUser *)user { + RLMSyncConfiguration *syncConfig = [[RLMSyncConfiguration alloc] initWithUser:user + realmURL:user.defaultRealmURL + customFileURL:nil + isPartial:YES + stopPolicy:RLMSyncStopPolicyAfterChangesUploaded + errorHandler:nullptr]; + RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init]; + config.syncConfiguration = syncConfig; + return config; +} + @end diff --git a/Pods/Realm/Realm/RLMSyncCredentials.m b/Pods/Realm/Realm/RLMSyncCredentials.m index 03669e6..a1521a2 100644 --- a/Pods/Realm/Realm/RLMSyncCredentials.m +++ b/Pods/Realm/Realm/RLMSyncCredentials.m @@ -29,6 +29,9 @@ RLMIdentityProvider const RLMIdentityProviderTwitter = @"twitter"; RLMIdentityProvider const RLMIdentityProviderGoogle = @"google"; RLMIdentityProvider const RLMIdentityProviderCloudKit = @"cloudkit"; +RLMIdentityProvider const RLMIdentityProviderJWT = @"jwt"; +RLMIdentityProvider const RLMIdentityProviderAnonymous = @"anonymous"; +RLMIdentityProvider const RLMIdentityProviderNickname = @"nickname"; @interface RLMSyncCredentials () @@ -65,6 +68,27 @@ + (instancetype)credentialsWithUsername:(NSString *)username kRLMSyncRegisterKey: @(shouldRegister)}]; } ++ (instancetype)credentialsWithJWT:(NSString *)token { + return [[self alloc] initWithCustomToken:token provider:RLMIdentityProviderJWT userInfo:nil]; +} + ++ (instancetype)anonymousCredentials { + return [[self alloc] initWithCustomToken:@"" provider:RLMIdentityProviderAnonymous userInfo:nil]; +} + ++ (instancetype)credentialsWithNickname:(NSString *)nickname isAdmin:(BOOL)isAdmin { + return [[self alloc] initWithCustomToken:nickname + provider:RLMIdentityProviderNickname + userInfo:@{kRLMSyncIsAdminKey: @(isAdmin), kRLMSyncDataKey: nickname}]; +} + +/// Intended only for testing use. Will only work if the ROS is started with the `debug` provider enabled. ++ (instancetype)credentialsWithDebugUserID:(NSString *)userID isAdmin:(BOOL)isAdmin { + return [[self alloc] initWithCustomToken:userID + provider:RLMIdentityProviderDebug + userInfo:@{kRLMSyncIsAdminKey: @(isAdmin)}]; +} + + (instancetype)credentialsWithAccessToken:(RLMServerToken)accessToken identity:(NSString *)identity { return [[self alloc] initWithCustomToken:accessToken provider:RLMIdentityProviderAccessToken diff --git a/Pods/Realm/Realm/RLMSyncErrorResponseModel.m b/Pods/Realm/Realm/RLMSyncErrorResponseModel.m deleted file mode 100644 index 5e75139..0000000 --- a/Pods/Realm/Realm/RLMSyncErrorResponseModel.m +++ /dev/null @@ -1,48 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#import "RLMSyncErrorResponseModel.h" - -static const NSString *const kRLMSyncErrorStatusKey = @"status"; -static const NSString *const kRLMSyncErrorCodeKey = @"code"; -static const NSString *const kRLMSyncErrorTitleKey = @"title"; -static const NSString *const kRLMSyncErrorHintKey = @"hint"; - -@interface RLMSyncErrorResponseModel () - -@property (nonatomic, readwrite) NSInteger status; -@property (nonatomic, readwrite) NSInteger code; -@property (nonatomic, readwrite) NSString *title; -@property (nonatomic, readwrite) NSString *hint; - -@end - -@implementation RLMSyncErrorResponseModel - -- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary { - if (self = [super init]) { - RLM_SYNC_PARSE_DOUBLE_OR_ABORT(jsonDictionary, kRLMSyncErrorStatusKey, status); - RLM_SYNC_PARSE_DOUBLE_OR_ABORT(jsonDictionary, kRLMSyncErrorCodeKey, code); - RLM_SYNC_PARSE_OPTIONAL_STRING(jsonDictionary, kRLMSyncErrorTitleKey, title); - RLM_SYNC_PARSE_OPTIONAL_STRING(jsonDictionary, kRLMSyncErrorHintKey, hint); - return self; - } - return nil; -} - -@end diff --git a/Pods/Realm/Realm/RLMSyncManager.mm b/Pods/Realm/Realm/RLMSyncManager.mm index ea3d4bc..b3be05e 100644 --- a/Pods/Realm/Realm/RLMSyncManager.mm +++ b/Pods/Realm/Realm/RLMSyncManager.mm @@ -22,6 +22,7 @@ #import "RLMSyncConfiguration_Private.hpp" #import "RLMSyncSession_Private.hpp" #import "RLMSyncUser_Private.hpp" +#import "RLMSyncUtil_Private.hpp" #import "RLMUtil.hpp" #import "sync/sync_config.hpp" @@ -86,17 +87,24 @@ - (instancetype)initWithCustomRootDirectory:(nullable NSURL *)rootDirectory NS_D @implementation RLMSyncManager static RLMSyncManager *s_sharedManager = nil; -static dispatch_once_t s_onceToken; + (instancetype)sharedManager { - dispatch_once(&s_onceToken, ^{ - s_sharedManager = [[RLMSyncManager alloc] initWithCustomRootDirectory:nil]; + static std::once_flag flag; + std::call_once(flag, [] { + try { + s_sharedManager = [[RLMSyncManager alloc] initWithCustomRootDirectory:nil]; + } + catch (std::exception const& e) { + @throw RLMException(e); + } }); return s_sharedManager; } - (instancetype)initWithCustomRootDirectory:(NSURL *)rootDirectory { if (self = [super init]) { + [RLMSyncUser _setUpBindingContextFactory]; + // Initialize the sync engine. SyncManager::shared().set_logger_factory(s_syncLoggerFactory); bool should_encrypt = !getenv("REALM_DISABLE_METADATA_ENCRYPTION") && !RLMIsRunningInPlayground(); @@ -125,14 +133,6 @@ - (void)setLogLevel:(RLMSyncLogLevel)logLevel { realm::SyncManager::shared().set_log_level(levelForSyncLogLevel(logLevel)); } -- (BOOL)disableSSLValidation { - return realm::SyncManager::shared().client_should_validate_ssl(); -} - -- (void)setDisableSSLValidation:(BOOL)disableSSLValidation { - realm::SyncManager::shared().set_client_should_validate_ssl(!disableSSLValidation); -} - #pragma mark - Private API - (void)_fireError:(NSError *)error { @@ -150,47 +150,38 @@ - (void)_fireErrorWithCode:(int)errorCode userInfo:(NSDictionary *)userInfo errorClass:(RLMSyncSystemErrorKind)errorClass { NSError *error = nil; - NSMutableDictionary *mutableUserInfo = [userInfo mutableCopy]; - mutableUserInfo[@"description"] = message; - mutableUserInfo[@"error"] = @(errorCode); - mutableUserInfo[@"underlying_class"] = @(errorClass); - + BOOL shouldMakeError = YES; + NSDictionary *custom = nil; + // Note that certain types of errors are 'interactive'; users have several options + // as to how to proceed after the error is reported. switch (errorClass) { case RLMSyncSystemErrorKindClientReset: { - // Client reset is a special case; the application can respond to it to a greater degree than - // it can for most other errors. - mutableUserInfo[kRLMSyncPathOfRealmBackupCopyKey] = userInfo[@(realm::SyncError::c_recovery_file_path_key)]; - std::string original_path = [userInfo[@(realm::SyncError::c_original_file_path_key)] UTF8String]; - mutableUserInfo[kRLMSyncInitiateClientResetBlockKey] = ^{ - SyncManager::shared().immediately_run_file_actions(original_path); - }; - error = [NSError errorWithDomain:RLMSyncErrorDomain - code:RLMSyncErrorClientResetError - userInfo:mutableUserInfo]; + std::string path = [userInfo[@(realm::SyncError::c_original_file_path_key)] UTF8String]; + custom = @{kRLMSyncPathOfRealmBackupCopyKey: + userInfo[@(realm::SyncError::c_recovery_file_path_key)], + kRLMSyncErrorActionTokenKey: + [[RLMSyncErrorActionToken alloc] initWithOriginalPath:std::move(path)] + };; break; } - case RLMSyncSystemErrorKindUser: - error = [NSError errorWithDomain:RLMSyncErrorDomain - code:RLMSyncErrorClientUserError - userInfo:mutableUserInfo]; + case RLMSyncSystemErrorKindPermissionDenied: { + std::string path = [userInfo[@(realm::SyncError::c_original_file_path_key)] UTF8String]; + custom = @{kRLMSyncErrorActionTokenKey: + [[RLMSyncErrorActionToken alloc] initWithOriginalPath:std::move(path)] + }; break; + } + case RLMSyncSystemErrorKindUser: case RLMSyncSystemErrorKindSession: - error = [NSError errorWithDomain:RLMSyncErrorDomain - code:RLMSyncErrorClientSessionError - userInfo:mutableUserInfo]; break; case RLMSyncSystemErrorKindConnection: case RLMSyncSystemErrorKindClient: - // Report the error. There's nothing the user can do about it, though. - if (fatal) { - error = [NSError errorWithDomain:RLMSyncErrorDomain - code:RLMSyncErrorClientInternalError - userInfo:mutableUserInfo]; - } - break; case RLMSyncSystemErrorKindUnknown: + // Report the error. There's nothing the user can do about it, though. + shouldMakeError = fatal; break; } + error = shouldMakeError ? make_sync_error(errorClass, message, errorCode, custom) : nil; dispatch_async(dispatch_get_main_queue(), ^{ if (!self.errorHandler || !error) { return; @@ -211,4 +202,11 @@ + (void)resetForTesting { SyncManager::shared().reset_for_testing(); } +- (RLMNetworkRequestOptions *)networkRequestOptions { + RLMNetworkRequestOptions *options = [[RLMNetworkRequestOptions alloc] init]; + options.authorizationHeaderName = self.authorizationHeaderName; + options.customHeaders = self.customRequestHeaders; + return options; +} + @end diff --git a/Pods/Realm/Realm/RLMSyncPermission.mm b/Pods/Realm/Realm/RLMSyncPermission.mm new file mode 100644 index 0000000..879a107 --- /dev/null +++ b/Pods/Realm/Realm/RLMSyncPermission.mm @@ -0,0 +1,382 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2017 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMSyncPermission_Private.hpp" + +#import "RLMArray.h" +#import "RLMObjectSchema.h" +#import "RLMRealm_Dynamic.h" +#import "RLMSyncUtil_Private.hpp" +#import "RLMUtil.hpp" +#import "RLMResults.h" + +using namespace realm; + +using ConditionType = Permission::Condition::Type; + +static void verifyInWriteTransaction(__unsafe_unretained RLMRealm *const realm, SEL sel) { + if (!realm) { + @throw RLMException(@"Cannot call %@ on an unmanaged object.", NSStringFromSelector(sel)); + } + if (!realm.inWriteTransaction) { + @throw RLMException(@"Cannot call %@ outside of a write transaction.", NSStringFromSelector(sel)); + } +} + +id RLMPermissionForRole(RLMArray *array, id role) { + RLMResults *filtered = [array objectsWhere:@"role.name = %@", [role name]]; + RLMPermission *permission; + for (RLMPermission *p in filtered) { + if (permission == nil) { + permission = p; + } + // If there's more than one permission for the role, merge it into the first + // one and then delete it as otherwise revoking permissions won't actually work + else { + if (p.canRead && !permission.canRead) { + permission.canRead = true; + } + if (p.canUpdate && !permission.canUpdate) { + permission.canUpdate = true; + } + if (p.canDelete && !permission.canDelete) { + permission.canDelete = true; + } + if (p.canSetPermissions && !permission.canSetPermissions) { + permission.canSetPermissions = true; + } + if (p.canQuery && !permission.canQuery) { + permission.canQuery = true; + } + if (p.canCreate && !permission.canCreate) { + permission.canCreate = true; + } + if (p.canModifySchema && !permission.canModifySchema) { + permission.canModifySchema = true; + } + [array.realm deleteObject:p]; + } + } + if (!permission) { + // Use the dynamic API to create the appropriate Permission class for the array + permission = (id)[array.realm createObject:array.objectClassName withValue:@[role]]; + [array addObject:permission]; + } + return permission; +} + +@implementation RLMPermissionRole ++ (NSString *)_realmObjectName { + return @"__Role"; +} ++ (NSString *)primaryKey { + return @"name"; +} ++ (NSArray *)requiredProperties { + return @[@"name"]; +} ++ (NSDictionary *)_realmColumnNames { + return @{@"users": @"members"}; +} +@end + +@implementation RLMPermissionUser ++ (NSString *)_realmObjectName { + return @"__User"; +} ++ (NSString *)primaryKey { + return @"identity"; +} ++ (NSArray *)requiredProperties { + return @[@"identity"]; +} ++ (NSDictionary *)_realmColumnNames { + return @{@"identity": @"id", @"role": @"role"}; +} ++ (NSDictionary *)linkingObjectsProperties { + return @{@"roles": [RLMPropertyDescriptor descriptorWithClass:RLMPermissionRole.class propertyName:@"users"]}; +} + ++ (RLMPermissionUser *)userInRealm:(RLMRealm *)realm withIdentity:(NSString *)identity { + return [self createOrUpdateInRealm:realm withValue:@[identity]]; +} +@end + +@implementation RLMPermission ++ (NSString *)_realmObjectName { + return @"__Permission"; +} ++ (NSDictionary *)defaultPropertyValues { + return @{@"canRead": @NO, + @"canUpdate": @NO, + @"canDelete": @NO, + @"canSetPermissions": @NO, + @"canQuery": @NO, + @"canCreate": @NO, + @"canModifySchema": @NO}; +} + ++ (RLMPermission *)permissionForRole:(RLMPermissionRole *)role inArray:(RLMArray *)array { + verifyInWriteTransaction(array.realm, _cmd); + auto index = [array indexOfObjectWhere:@"role = %@", role]; + if (index != NSNotFound) { + return array[index]; + } + RLMPermission *permission = [RLMPermission createInRealm:role.realm withValue:@[role]]; + [array addObject:permission]; + return permission; +} + ++ (RLMPermission *)permissionForRoleNamed:(NSString *)roleName inArray:(RLMArray *)array { + verifyInWriteTransaction(array.realm, _cmd); + return RLMPermissionForRole(array, [RLMPermissionRole createOrUpdateInRealm:array.realm withValue:@{@"name": roleName}]); +} + ++ (RLMPermission *)permissionForRoleNamed:(NSString *)roleName onRealm:(RLMRealm *)realm { + verifyInWriteTransaction(realm, _cmd); + return [self permissionForRoleNamed:roleName + inArray:[RLMRealmPermission objectInRealm:realm].permissions]; + +} + ++ (RLMPermission *)permissionForRoleNamed:(NSString *)roleName onClass:(Class)cls realm:(RLMRealm *)realm { + verifyInWriteTransaction(realm, _cmd); + return [self permissionForRoleNamed:roleName + inArray:[RLMClassPermission objectInRealm:realm forClass:cls].permissions]; +} + ++ (RLMPermission *)permissionForRoleNamed:(NSString *)roleName onClassNamed:(NSString *)className realm:(RLMRealm *)realm { + verifyInWriteTransaction(realm, _cmd); + return [self permissionForRoleNamed:roleName + inArray:[RLMClassPermission objectInRealm:realm forClassNamed:className].permissions]; +} + ++ (RLMPermission *)permissionForRoleNamed:(NSString *)roleName onObject:(RLMObject *)object { + verifyInWriteTransaction(object.realm, _cmd); + for (RLMProperty *prop in object.objectSchema.properties) { + if (prop.array && [prop.objectClassName isEqualToString:@"RLMPermission"]) { + return [self permissionForRoleNamed:roleName + inArray:[object valueForKey:prop.name]]; + } + } + @throw RLMException(@"Object %@ does not have a RLMArray property.", object); +} +@end + +@implementation RLMClassPermission ++ (NSString *)_realmObjectName { + return @"__Class"; +} ++ (NSString *)primaryKey { + return @"name"; +} ++ (NSArray *)requiredProperties { + return @[@"name"]; +} + ++ (instancetype)objectInRealm:(RLMRealm *)realm forClassNamed:(NSString *)name { + return [RLMClassPermission objectInRealm:realm forPrimaryKey:name]; +} ++ (instancetype)objectInRealm:(RLMRealm *)realm forClass:(Class)cls { + return [RLMClassPermission objectInRealm:realm forPrimaryKey:[cls _realmObjectName] ?: [cls className]]; +} +@end + +@interface RLMRealmPermission () +@property (nonatomic) int pk; +@end + +@implementation RLMRealmPermission ++ (NSString *)_realmObjectName { + return @"__Realm"; +} ++ (NSDictionary *)_realmColumnNames { + return @{@"pk": @"id"}; +} ++ (NSString *)primaryKey { + return @"pk"; +} + ++ (instancetype)objectInRealm:(RLMRealm *)realm { + return [RLMRealmPermission objectInRealm:realm forPrimaryKey:@0]; +} +@end + +#pragma mark - Permission + +@interface RLMSyncPermission () { +@private + NSString *_identity; + NSString *_key; + NSString *_value; + util::Optional _underlying; + RLMSyncAccessLevel _accessLevel; + NSString *_path; + NSDate *_updatedAt; +} +@end + +@implementation RLMSyncPermission + +- (instancetype)initWithRealmPath:(NSString *)path + identity:(NSString *)identity + accessLevel:(RLMSyncAccessLevel)accessLevel { + if (self = [super init]) { + _accessLevel = accessLevel; + _path = path; + _identity = identity; + if (!identity) { + @throw RLMException(@"A permission value cannot be created without a valid user ID"); + } + _updatedAt = [NSDate date]; + } + return self; +} + +- (instancetype)initWithRealmPath:(NSString *)path + username:(NSString *)username + accessLevel:(RLMSyncAccessLevel)accessLevel { + if (self = [super init]) { + _accessLevel = accessLevel; + _path = path; + _identity = nil; + _key = @"email"; + _value = username; + _updatedAt = [NSDate date]; + } + return self; +} + +- (instancetype)initWithPermission:(Permission)permission { + if (self = [super init]) { + _underlying = util::make_optional(std::move(permission)); + return self; + } + return nil; +} + +- (NSString *)path { + if (!_underlying) { + REALM_ASSERT(_path); + return _path; + } + return @(_underlying->path.c_str()); +} + +- (RLMSyncAccessLevel)accessLevel { + if (!_underlying) { + return _accessLevel; + } + return objCAccessLevelForAccessLevel(_underlying->access); +} + +- (BOOL)mayRead { + return self.accessLevel > RLMSyncAccessLevelNone; +} + +- (BOOL)mayWrite { + return self.accessLevel > RLMSyncAccessLevelRead; +} + +- (BOOL)mayManage { + return self.accessLevel == RLMSyncAccessLevelAdmin; +} + +- (NSString *)identity { + if (!_underlying) { + return _identity; + } + if (_underlying->condition.type == ConditionType::UserId) { + return @(_underlying->condition.user_id.c_str()); + } + return nil; +} + +- (NSString *)key { + if (!_underlying) { + return _key; + } + if (_underlying->condition.type == ConditionType::KeyValue) { + return @(_underlying->condition.key_value.first.c_str()); + } + return nil; +} + +- (NSString *)value { + if (!_underlying) { + return _value; + } + if (_underlying->condition.type == ConditionType::KeyValue) { + return @(_underlying->condition.key_value.second.c_str()); + } + return nil; +} + +- (NSDate *)updatedAt { + if (!_underlying) { + return _updatedAt; + } + return RLMTimestampToNSDate(_underlying->updated_at); +} + +- (realm::Permission)rawPermission { + if (_underlying) { + return *_underlying; + } + auto condition = (_identity + ? Permission::Condition([_identity UTF8String]) + : Permission::Condition([_key UTF8String], [_value UTF8String])); + return Permission{ + [_path UTF8String], + accessLevelForObjCAccessLevel(_accessLevel), + std::move(condition) + }; +} + +- (NSUInteger)hash { + return [self.identity hash] ^ self.accessLevel; +} + +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if ([object isKindOfClass:[RLMSyncPermission class]]) { + RLMSyncPermission *that = (RLMSyncPermission *)object; + return (self.accessLevel == that.accessLevel + && Permission::paths_are_equivalent([self.path UTF8String], [that.path UTF8String], + [self.identity UTF8String], [that.identity UTF8String]) + && [self.identity isEqualToString:that.identity]); + } + return NO; +} + +- (NSString *)description { + NSString *typeDescription = nil; + if (self.identity) { + typeDescription = [NSString stringWithFormat:@"identity: %@", self.identity]; + } else { + typeDescription = [NSString stringWithFormat:@"key: %@, value: %@", self.key, self.value]; + } + return [NSString stringWithFormat:@" %@, path: %@, access level: %@", + typeDescription, + self.path, + @(Permission::description_for_access_level(accessLevelForObjCAccessLevel(self.accessLevel)).c_str())]; +} + +@end diff --git a/Pods/Realm/Realm/RLMSyncPermissionChange.m b/Pods/Realm/Realm/RLMSyncPermissionChange.m deleted file mode 100644 index 55ba2fe..0000000 --- a/Pods/Realm/Realm/RLMSyncPermissionChange.m +++ /dev/null @@ -1,69 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#import "RLMSyncPermissionChange_Private.h" -#import "RLMSyncUtil_Private.h" - -@implementation RLMSyncPermissionChange - -+ (instancetype)permissionChangeWithRealmURL:(NSString *)realmURL - userID:(NSString *)userID - read:(nullable NSNumber *)mayRead - write:(nullable NSNumber *)mayWrite - manage:(nullable NSNumber *)mayManage { - RLMSyncPermissionChange *permissionChange = [RLMSyncPermissionChange new]; - permissionChange.realmUrl = realmURL; - permissionChange.userId = userID; - permissionChange.mayRead = mayRead; - permissionChange.mayWrite = mayWrite; - permissionChange.mayManage = mayManage; - return permissionChange; -} - -+ (NSArray *)requiredProperties { - return @[@"id", @"createdAt", @"updatedAt", @"realmUrl", @"userId"]; -} - -+ (NSDictionary *)defaultPropertyValues { - NSDate *now = [NSDate date]; - return @{ - @"id": [NSUUID UUID].UUIDString, - @"createdAt": now, - @"updatedAt": now, - @"realmUrl": @"*", - @"userId": @"*" - }; -} - -+ (nullable NSString *)primaryKey { - return @"id"; -} - -+ (BOOL)shouldIncludeInDefaultSchema { - return NO; -} - -- (RLMSyncManagementObjectStatus)status { - return RLMMakeSyncManagementObjectStatus(self.statusCode); -} - -+ (NSString *)_realmObjectName { - return @"PermissionChange"; -} - -@end diff --git a/Pods/Realm/Realm/RLMSyncPermissionOffer.m b/Pods/Realm/Realm/RLMSyncPermissionOffer.m deleted file mode 100644 index 6f38a78..0000000 --- a/Pods/Realm/Realm/RLMSyncPermissionOffer.m +++ /dev/null @@ -1,72 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#import "RLMSyncPermissionOffer_Private.h" -#import "RLMSyncUtil_Private.h" - -@implementation RLMSyncPermissionOffer - -+ (instancetype)permissionOfferWithRealmURL:(NSString *)realmURL - expiresAt:(nullable NSDate *)expiresAt - read:(BOOL)mayRead - write:(BOOL)mayWrite - manage:(BOOL)mayManage { - RLMSyncPermissionOffer *permissionOffer = [RLMSyncPermissionOffer new]; - permissionOffer.realmUrl = realmURL; - permissionOffer.expiresAt = expiresAt; - permissionOffer.mayRead = mayRead; - permissionOffer.mayWrite = mayWrite; - permissionOffer.mayManage = mayManage; - return permissionOffer; -} - -+ (NSArray *)requiredProperties { - return @[@"id", @"createdAt", @"updatedAt", @"realmUrl"]; -} - -+ (NSArray *)indexedProperties { - return @[@"token"]; -} - -+ (NSDictionary *)defaultPropertyValues { - NSDate *now = [NSDate date]; - return @{ - @"id": [NSUUID UUID].UUIDString, - @"createdAt": now, - @"updatedAt": now, - @"realmUrl": @"" - }; -} - -+ (nullable NSString *)primaryKey { - return @"id"; -} - -+ (BOOL)shouldIncludeInDefaultSchema { - return NO; -} - -- (RLMSyncManagementObjectStatus)status { - return RLMMakeSyncManagementObjectStatus(self.statusCode); -} - -+ (NSString *)_realmObjectName { - return @"PermissionOffer"; -} - -@end diff --git a/Pods/Realm/Realm/RLMSyncPermissionOfferResponse.m b/Pods/Realm/Realm/RLMSyncPermissionOfferResponse.m deleted file mode 100644 index ab7b685..0000000 --- a/Pods/Realm/Realm/RLMSyncPermissionOfferResponse.m +++ /dev/null @@ -1,60 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#import "RLMSyncPermissionOfferResponse_Private.h" -#import "RLMSyncUtil_Private.h" - -@implementation RLMSyncPermissionOfferResponse - -+ (instancetype)permissionOfferResponseWithToken:(NSString *)token { - RLMSyncPermissionOfferResponse *permissionOfferResponse = [RLMSyncPermissionOfferResponse new]; - permissionOfferResponse.token = token; - return permissionOfferResponse; -} - -+ (NSArray *)requiredProperties { - return @[@"id", @"createdAt", @"updatedAt", @"token"]; -} - -+ (NSDictionary *)defaultPropertyValues { - NSDate *now = [NSDate date]; - return @{ - @"id": [NSUUID UUID].UUIDString, - @"createdAt": now, - @"updatedAt": now, - @"token": @"", - }; -} - -+ (nullable NSString *)primaryKey { - return @"id"; -} - -+ (BOOL)shouldIncludeInDefaultSchema { - return NO; -} - -- (RLMSyncManagementObjectStatus)status { - return RLMMakeSyncManagementObjectStatus(self.statusCode); -} - -+ (NSString *)_realmObjectName { - return @"PermissionOfferResponse"; -} - -@end diff --git a/Pods/Realm/Realm/RLMSyncPermissionResults.mm b/Pods/Realm/Realm/RLMSyncPermissionResults.mm new file mode 100644 index 0000000..da3bcb9 --- /dev/null +++ b/Pods/Realm/Realm/RLMSyncPermissionResults.mm @@ -0,0 +1,258 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2017 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMSyncPermissionResults.h" + +#import "RLMCollection_Private.hpp" +#import "RLMObjectSchema_Private.hpp" +#import "RLMQueryUtil.hpp" +#import "RLMResults_Private.hpp" +#import "RLMSchema_Private.hpp" +#import "RLMSyncPermission_Private.hpp" +#import "RLMSyncUtil_Private.hpp" +#import "RLMUtil.hpp" + +#import "object.hpp" + +using namespace realm; + +namespace { + +bool keypath_is_valid(NSString *keypath) +{ + static auto valid = [NSSet setWithArray:@[RLMSyncPermissionSortPropertyPath, + RLMSyncPermissionSortPropertyUserID, + RLMSyncPermissionSortPropertyUpdated]]; + return [valid containsObject:keypath]; +} + +} + +/// Sort by the Realm Object Server path to the Realm to which the permission applies. +RLMSyncPermissionSortProperty const RLMSyncPermissionSortPropertyPath = @"path"; +/// Sort by the identity of the user to whom the permission applies. +RLMSyncPermissionSortProperty const RLMSyncPermissionSortPropertyUserID = @"userId"; +/// Sort by the date the permissions were last updated. +RLMSyncPermissionSortProperty const RLMSyncPermissionSortPropertyUpdated = @"updatedAt"; + +@interface RLMSyncPermissionResults () +@property (nonatomic, strong) RLMSchema *schema; +@property (nonatomic, strong) RLMObjectSchema *objectSchema; +@end + +@implementation RLMSyncPermissionResults + +#pragma mark - Public API + +- (RLMPropertyType)type { + return RLMPropertyTypeObject; +} + +- (NSString *)objectClassName { + return NSStringFromClass([RLMSyncPermission class]); +} + +- (RLMRealm *)realm { + return nil; +} + +- (RLMSyncPermission *)objectAtIndex:(NSUInteger)index { + return translateRLMResultsErrors([&] { + Object permission(_results.get_realm(), _results.get_object_schema(), _results.get(index)); + return [[RLMSyncPermission alloc] initWithPermission:Permission(permission)]; + }); +} + +- (RLMSyncPermission *)firstObject { + return self.count == 0 ? nil : [self objectAtIndex:0]; +} + +- (RLMSyncPermission *)lastObject { + return self.count == 0 ? nil : [self objectAtIndex:(self.count - 1)]; +} + +- (NSUInteger)indexOfObject:(RLMSyncPermission *)object { + if (object.key) { + // Key-value permissions are only used for setting; they are never returned. + return NSNotFound; + } + // Canonicalize the path. + NSString *path = object.path; + if ([path rangeOfString:@"~"].location != NSNotFound) { + path = [path stringByReplacingOccurrencesOfString:@"~" withString:object.identity]; + } + NSString *topPrivilege; + switch (object.accessLevel) { + case RLMSyncAccessLevelNone: + // Deleted permissions are removed from the permissions Realm by ROS. + return NSNotFound; + case RLMSyncAccessLevelRead: + topPrivilege = @"mayRead"; + break; + case RLMSyncAccessLevelWrite: + topPrivilege = @"mayWrite"; + break; + case RLMSyncAccessLevelAdmin: + topPrivilege = @"mayManage"; + break; + } + // Build the predicate. + NSPredicate *p = [NSPredicate predicateWithFormat:@"%K = %@ AND %K = %@ AND %K == YES", + RLMSyncPermissionSortPropertyPath, path, + RLMSyncPermissionSortPropertyUserID, object.identity, + topPrivilege]; + return [self indexOfObjectWithPredicate:p]; +} + +- (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate { + return translateRLMResultsErrors([&] { + auto& group = _results.get_realm()->read_group(); + auto query = RLMPredicateToQuery(predicate, self.objectSchema, self.schema, group); + return RLMConvertNotFound(_results.index_of(std::move(query))); + }); +} + +- (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate { + return translateRLMResultsErrors([&] { + auto query = RLMPredicateToQuery(predicate, self.objectSchema, self.schema, _results.get_realm()->read_group()); + return [[RLMSyncPermissionResults alloc] initWithResults:_results.filter(std::move(query))]; + }); +} + +- (RLMResults *)sortedResultsUsingDescriptors:(NSArray *)properties { + if (properties.count == 0) { + return self; + } + for (RLMSortDescriptor *descriptor in properties) { + if (!keypath_is_valid(descriptor.keyPath)) { + @throw RLMException(@"Invalid keypath specified. Use one of the constants defined in " + @" `RLMSyncPermissionSortProperty`."); + } + } + return translateRLMResultsErrors([&] { + auto sorted = _results.sort(RLMSortDescriptorsToKeypathArray(properties)); + return [[RLMSyncPermissionResults alloc] initWithResults:std::move(sorted)]; + }); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmismatched-parameter-types" +- (RLMNotificationToken *)addNotificationBlock:(void(^)(RLMSyncPermissionResults *results, + RLMCollectionChange *change, + NSError *error))block { + auto cb = [=](const realm::CollectionChangeSet& changes, std::exception_ptr ptr) { + if (ptr) { + NSError *error = translateSyncExceptionPtrToError(std::move(ptr), RLMPermissionActionTypeGet); + REALM_ASSERT(error); + block(nil, nil, error); + } else { + // Finished successfully + block(self, [[RLMCollectionChange alloc] initWithChanges:changes], nil); + } + }; + return [[RLMCancellationToken alloc] initWithToken:_results.add_notification_callback(std::move(cb)) realm:nil]; +} +#pragma clang diagnostic pop + +- (id)aggregate:(__unused NSString *)property + method:(__unused util::Optional (Results::*)(size_t))method + methodName:(__unused NSString *)methodName returnNilForEmpty:(__unused BOOL)returnNilForEmpty { + // We don't support any of the min/max/average/sum APIs; they don't make sense for this collection type. + return nil; +} + +- (id)valueForKey:(NSString *)key { + size_t count = self.count; + if (count == 0) { + return @[]; + } + NSMutableArray *results = [NSMutableArray arrayWithCapacity:count]; + if ([key isEqualToString:@"self"]) { + for (size_t i = 0; i < count; i++) { + [results addObject:[self objectAtIndex:i]]; + } + } else { + for (size_t i = 0; i < count; i++) { + [results addObject:[[self objectAtIndex:i] valueForKey:key] ?: NSNull.null]; + } + } + return results; +} + +- (void)setValue:(__unused id)value forKey:(__unused NSString *)key { + @throw RLMException(@"Cannot set values for the read-only type `RLMSyncPermission`."); +} + +#pragma mark - System + +- (RLMSchema *)schema { + if (!_schema) { + _schema = [RLMSchema dynamicSchemaFromObjectStoreSchema:_results.get_realm()->schema()]; + } + return _schema; +} + +- (RLMObjectSchema *)objectSchema { + if (!_objectSchema) { + _objectSchema = [RLMObjectSchema objectSchemaForObjectStoreSchema:_results.get_object_schema()]; + } + return _objectSchema; +} + +- (NSString *)description { + return RLMDescriptionWithMaxDepth(@"RLMSyncPermissionResults", self, 1); +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state + objects:(id __unsafe_unretained [])buffer + count:(NSUInteger)len { + // FIXME: It would be nice to have a shared fast enumeration implementation for `realm::Results`-only RLMResults. + NSUInteger thisSize = self.count; + if (state->state == 0) { + state->extra[0] = 0; + state->extra[1] = (long)thisSize; + state->state = 1; + } + NSUInteger objectsInBuffer = 0; + long idx = state->extra[0]; + if ((unsigned long)idx == thisSize) { + // finished + return 0; + } + state->itemsPtr = buffer; + state->mutationsPtr = state->extra + 1; + while (true) { + if (objectsInBuffer == len) { + // Buffer is full. + state->extra[0] = idx; + return objectsInBuffer; + } + if ((unsigned long)idx == thisSize) { + // finished + state->extra[0] = idx; + return objectsInBuffer; + } + // Otherwise, add an object and advance the index pointer. + RLMSyncPermission * __autoreleasing thisPermission = [self objectAtIndex:idx]; + buffer[objectsInBuffer] = thisPermission; + idx++; + objectsInBuffer++; + } +} + +@end diff --git a/Pods/Realm/Realm/RLMSyncSession.mm b/Pods/Realm/Realm/RLMSyncSession.mm index 40ca6df..14a41d5 100644 --- a/Pods/Realm/Realm/RLMSyncSession.mm +++ b/Pods/Realm/Realm/RLMSyncSession.mm @@ -18,12 +18,21 @@ #import "RLMSyncSession_Private.hpp" +#import "RLMRealm_Private.hpp" #import "RLMSyncConfiguration_Private.hpp" #import "RLMSyncUser_Private.hpp" +#import "RLMSyncUtil_Private.hpp" #import "sync/sync_session.hpp" using namespace realm; +@interface RLMSyncErrorActionToken () { +@public + std::string _originalPath; + BOOL _isValid; +} +@end + @interface RLMProgressNotificationToken() { uint64_t _token; std::weak_ptr _session; @@ -37,7 +46,7 @@ - (void)suppressNextNotification { // `-[RLMRealm commitWriteTransactionWithoutNotifying:]`. } -- (void)stop { +- (void)invalidate { if (auto session = _session.lock()) { session->unregister_progress_notifier(_token); _session.reset(); @@ -49,7 +58,7 @@ - (void)dealloc { if (_token != 0) { NSLog(@"RLMProgressNotificationToken released without unregistering a notification. " @"You must hold on to the RLMProgressNotificationToken and call " - @"-[RLMProgressNotificationToken stop] when you no longer wish to receive " + @"-[RLMProgressNotificationToken invalidate] when you no longer wish to receive " @"progress update notifications."); } } @@ -131,28 +140,34 @@ - (RLMSyncSessionState)state { return RLMSyncSessionStateInvalid; } -- (BOOL)waitForUploadCompletionOnQueue:(dispatch_queue_t)queue callback:(void(^)(void))callback { +- (BOOL)waitForUploadCompletionOnQueue:(dispatch_queue_t)queue callback:(void(^)(NSError *))callback { if (auto session = _session.lock()) { if (session->state() == SyncSession::PublicState::Error) { return NO; } queue = queue ?: dispatch_get_main_queue(); - session->wait_for_upload_completion([=](std::error_code) { // FIXME: report error to user - dispatch_async(queue, callback); + session->wait_for_upload_completion([=](std::error_code err) { + NSError *error = (err == std::error_code{}) ? nil : make_sync_error(err); + dispatch_async(queue, ^{ + callback(error); + }); }); return YES; } return NO; } -- (BOOL)waitForDownloadCompletionOnQueue:(dispatch_queue_t)queue callback:(void(^)(void))callback { +- (BOOL)waitForDownloadCompletionOnQueue:(dispatch_queue_t)queue callback:(void(^)(NSError *))callback { if (auto session = _session.lock()) { if (session->state() == SyncSession::PublicState::Error) { return NO; } queue = queue ?: dispatch_get_main_queue(); - session->wait_for_download_completion([=](std::error_code) { // FIXME: report error to user - dispatch_async(queue, callback); + session->wait_for_download_completion([=](std::error_code err) { + NSError *error = (err == std::error_code{}) ? nil : make_sync_error(err); + dispatch_async(queue, ^{ + callback(error); + }); }); return YES; } @@ -160,7 +175,7 @@ - (BOOL)waitForDownloadCompletionOnQueue:(dispatch_queue_t)queue callback:(void( } - (RLMProgressNotificationToken *)addProgressNotificationForDirection:(RLMSyncProgressDirection)direction - mode:(RLMSyncProgress)mode + mode:(RLMSyncProgressMode)mode block:(RLMProgressNotificationBlock)block { if (auto session = _session.lock()) { if (session->state() == SyncSession::PublicState::Error) { @@ -170,7 +185,7 @@ - (RLMProgressNotificationToken *)addProgressNotificationForDirection:(RLMSyncPr auto notifier_direction = (direction == RLMSyncProgressDirectionUpload ? SyncSession::NotifierType::upload : SyncSession::NotifierType::download); - bool is_streaming = (mode == RLMSyncProgressReportIndefinitely); + bool is_streaming = (mode == RLMSyncProgressModeReportIndefinitely); uint64_t token = session->register_progress_notifier([=](uint64_t transferred, uint64_t transferrable) { dispatch_async(queue, ^{ block((NSUInteger)transferred, (NSUInteger)transferrable); @@ -181,4 +196,39 @@ - (RLMProgressNotificationToken *)addProgressNotificationForDirection:(RLMSyncPr return nil; } ++ (void)immediatelyHandleError:(RLMSyncErrorActionToken *)token { + if (!token->_isValid) { + return; + } + token->_isValid = NO; + SyncManager::shared().immediately_run_file_actions(std::move(token->_originalPath)); +} + ++ (nullable RLMSyncSession *)sessionForRealm:(RLMRealm *)realm { + auto& config = realm->_realm->config().sync_config; + if (!config) { + return nil; + } + auto path = SyncManager::shared().path_for_realm(*config->user, config->realm_url()); + if (auto session = config->user->session_for_on_disk_path(path)) { + return [[RLMSyncSession alloc] initWithSyncSession:session]; + } + return nil; +} + +@end + +// MARK: - Error action token + +@implementation RLMSyncErrorActionToken + +- (instancetype)initWithOriginalPath:(std::string)originalPath { + if (self = [super init]) { + _isValid = YES; + _originalPath = std::move(originalPath); + return self; + } + return nil; +} + @end diff --git a/Pods/Realm/Realm/RLMSyncSessionRefreshHandle.mm b/Pods/Realm/Realm/RLMSyncSessionRefreshHandle.mm index dae423e..1353645 100644 --- a/Pods/Realm/Realm/RLMSyncSessionRefreshHandle.mm +++ b/Pods/Realm/Realm/RLMSyncSessionRefreshHandle.mm @@ -18,27 +18,50 @@ #import "RLMSyncSessionRefreshHandle.hpp" -#import "RLMAuthResponseModel.h" +#import "RLMJSONModels.h" #import "RLMNetworkClient.h" #import "RLMSyncManager_Private.h" #import "RLMSyncUser_Private.hpp" -#import "RLMTokenModels.h" +#import "RLMSyncUtil_Private.hpp" #import "RLMUtil.hpp" #import "sync/sync_session.hpp" using namespace realm; +namespace { + +void unregisterRefreshHandle(const std::weak_ptr& user, const std::string& path) { + if (auto strong_user = user.lock()) { + context_for(strong_user).unregister_refresh_handle(path); + } +} + +void reportInvalidAccessToken(const std::weak_ptr& user, NSError *error) { + if (auto strong_user = user.lock()) { + if (RLMUserErrorReportingBlock block = context_for(strong_user).error_handler()) { + RLMSyncUser *theUser = [[RLMSyncUser alloc] initWithSyncUser:std::move(strong_user)]; + [theUser logOut]; + block(theUser, error); + } + } +} + +} + +static const NSTimeInterval RLMRefreshBuffer = 10; + @interface RLMSyncSessionRefreshHandle () { + std::weak_ptr _user; + std::string _path; std::weak_ptr _session; std::shared_ptr _strongSession; } -@property (nonatomic, weak) RLMSyncUser *user; -@property (nonatomic, strong) NSString *pathToRealm; @property (nonatomic) NSTimer *timer; @property (nonatomic) NSURL *realmURL; +@property (nonatomic) NSURL *authServerURL; @property (nonatomic, copy) RLMSyncBasicErrorReportingBlock completionBlock; @end @@ -46,18 +69,22 @@ @interface RLMSyncSessionRefreshHandle () { @implementation RLMSyncSessionRefreshHandle - (instancetype)initWithRealmURL:(NSURL *)realmURL - user:(RLMSyncUser *)user + user:(std::shared_ptr)user session:(std::shared_ptr)session completionBlock:(RLMSyncBasicErrorReportingBlock)completionBlock { if (self = [super init]) { NSString *path = [realmURL path]; - self.pathToRealm = path; - self.user = user; + _path = [path UTF8String]; + self.authServerURL = [NSURL URLWithString:@(user->server_url().c_str())]; + if (!self.authServerURL) { + @throw RLMException(@"User object isn't configured with an auth server URL."); + } self.completionBlock = completionBlock; self.realmURL = realmURL; // For the initial bind, we want to prolong the session's lifetime. _strongSession = std::move(session); _session = _strongSession; + _user = user; // Immediately fire off the network request. [self _timerFired:nil]; return self; @@ -75,8 +102,7 @@ - (void)invalidate { } + (NSDate *)fireDateForTokenExpirationDate:(NSDate *)date nowDate:(NSDate *)nowDate { - static const NSTimeInterval refreshBuffer = 10; - NSDate *fireDate = [date dateByAddingTimeInterval:-refreshBuffer]; + NSDate *fireDate = [date dateByAddingTimeInterval:-RLMRefreshBuffer]; // Only fire times in the future are valid. return ([fireDate compare:nowDate] == NSOrderedDescending ? fireDate : nil); } @@ -92,7 +118,7 @@ - (void)scheduleRefreshTimer:(NSDate *)dateWhenTokenExpires { NSDate *fireDate = [RLMSyncSessionRefreshHandle fireDateForTokenExpirationDate:dateWhenTokenExpires nowDate:[NSDate date]]; if (!fireDate) { - [self.user _unregisterRefreshHandleForURLPath:self.pathToRealm]; + unregisterRefreshHandle(_user, _path); return; } self.timer = [[NSTimer alloc] initWithFireDate:fireDate @@ -106,12 +132,12 @@ - (void)scheduleRefreshTimer:(NSDate *)dateWhenTokenExpires { } /// Handler for network requests whose responses successfully parse into an auth response model. -- (BOOL)_handleSuccessfulRequest:(RLMAuthResponseModel *)model strongUser:(RLMSyncUser *)user { +- (BOOL)_handleSuccessfulRequest:(RLMAuthResponseModel *)model { // Success std::shared_ptr session = _session.lock(); if (!session) { // The session is dead or in a fatal error state. - [user _unregisterRefreshHandleForURLPath:self.pathToRealm]; + unregisterRefreshHandle(_user, _path); [self invalidate]; return NO; } @@ -139,38 +165,35 @@ - (BOOL)_handleSuccessfulRequest:(RLMAuthResponseModel *)model strongUser:(RLMSy [self scheduleRefreshTimer:expires]; } else { // The session is dead or in a fatal error state. - [user _unregisterRefreshHandleForURLPath:self.pathToRealm]; + unregisterRefreshHandle(_user, _path); [self invalidate]; } } if (self.completionBlock) { - self.completionBlock(success ? nil : [NSError errorWithDomain:RLMSyncErrorDomain - code:RLMSyncErrorClientSessionError - userInfo:nil]); + self.completionBlock(success ? nil : make_auth_error_client_issue()); } return success; } /// Handler for network requests that failed before the JSON parsing stage. -- (BOOL)_handleFailedRequest:(NSError *)error strongUser:(RLMSyncUser *)user { - NSError *syncError; - if ([error.domain isEqualToString:RLMSyncErrorDomain]) { +- (void)_handleFailedRequest:(NSError *)error { + NSError *authError; + if ([error.domain isEqualToString:RLMSyncAuthErrorDomain]) { // Network client may return sync related error - syncError = error; + authError = error; + // Try to report this error to the expiration callback. + reportInvalidAccessToken(_user, authError); } else { // Something else went wrong - syncError = [NSError errorWithDomain:RLMSyncErrorDomain - code:RLMSyncErrorBadResponse - userInfo:@{kRLMSyncUnderlyingErrorKey: error}]; + authError = make_auth_error_bad_response(); } - if (self.completionBlock) { - self.completionBlock(syncError); + self.completionBlock(authError); } - [[RLMSyncManager sharedManager] _fireError:syncError]; + [[RLMSyncManager sharedManager] _fireError:make_sync_error(authError)]; // Certain errors related to network connectivity should trigger a retry. NSDate *nextTryDate = nil; - if (error.domain == NSURLErrorDomain) { + if ([error.domain isEqualToString:NSURLErrorDomain]) { switch (error.code) { case NSURLErrorCannotConnectToHost: case NSURLErrorNotConnectedToInternet: @@ -179,7 +202,7 @@ - (BOOL)_handleFailedRequest:(NSError *)error strongUser:(RLMSyncUser *)user { case NSURLErrorDNSLookupFailed: case NSURLErrorCannotFindHost: // FIXME: 10 seconds is an arbitrarily chosen value, consider rationalizing it. - nextTryDate = [NSDate dateWithTimeIntervalSinceNow:10]; + nextTryDate = [NSDate dateWithTimeIntervalSinceNow:RLMRefreshBuffer + 10]; break; default: break; @@ -187,63 +210,59 @@ - (BOOL)_handleFailedRequest:(NSError *)error strongUser:(RLMSyncUser *)user { } if (!nextTryDate) { // This error isn't a network failure error. Just invalidate the refresh handle and stop. - [user _unregisterRefreshHandleForURLPath:self.pathToRealm]; + if (_strongSession) { + _strongSession->log_out(); + } + unregisterRefreshHandle(_user, _path); [self invalidate]; - return NO; + return; } // If we tried to initially bind the session and failed, we'll try again. However, each // subsequent attempt will use a weak pointer to avoid prolonging the session's lifetime // unnecessarily. _strongSession = nullptr; [self scheduleRefreshTimer:nextTryDate]; - return NO; + return; } /// Callback handler for network requests. - (BOOL)_onRefreshCompletionWithError:(NSError *)error json:(NSDictionary *)json { - RLMSyncUser *user = self.user; - if (!user) { - return NO; - } if (json && !error) { RLMAuthResponseModel *model = [[RLMAuthResponseModel alloc] initWithDictionary:json requireAccessToken:YES requireRefreshToken:NO]; if (model) { - return [self _handleSuccessfulRequest:model strongUser:user]; + return [self _handleSuccessfulRequest:model]; } // Otherwise, malformed JSON - error = [NSError errorWithDomain:RLMSyncErrorDomain - code:RLMSyncErrorBadResponse - userInfo:@{kRLMSyncErrorJSONKey: json}]; - [user _unregisterRefreshHandleForURLPath:self.pathToRealm]; + unregisterRefreshHandle(_user, _path); [self.timer invalidate]; + NSError *error = make_sync_error(make_auth_error_bad_response(json)); if (self.completionBlock) { self.completionBlock(error); } [[RLMSyncManager sharedManager] _fireError:error]; - return NO; } else { REALM_ASSERT(error); - return [self _handleFailedRequest:error strongUser:user]; + [self _handleFailedRequest:error]; } + return NO; } - (void)_timerFired:(__unused NSTimer *)timer { - RLMSyncUser *user = self.user; - if (!user) { - return; + RLMServerToken refreshToken = nil; + if (auto user = _user.lock()) { + refreshToken = @(user->refresh_token().c_str()); } - RLMServerToken refreshToken = user._refreshToken; if (!refreshToken) { - [user _unregisterRefreshHandleForURLPath:self.pathToRealm]; + unregisterRefreshHandle(_user, _path); [self.timer invalidate]; return; } NSDictionary *json = @{ kRLMSyncProviderKey: @"realm", - kRLMSyncPathKey: self.pathToRealm, + kRLMSyncPathKey: @(_path.c_str()), kRLMSyncDataKey: refreshToken, kRLMSyncAppIDKey: [RLMSyncManager sharedManager].appID, }; @@ -252,9 +271,11 @@ - (void)_timerFired:(__unused NSTimer *)timer { RLMSyncCompletionBlock handler = ^(NSError *error, NSDictionary *json) { [weakSelf _onRefreshCompletionWithError:error json:json]; }; - [RLMNetworkClient postRequestToEndpoint:RLMServerEndpointAuth - server:user.authenticationServer + [RLMNetworkClient sendRequestToEndpoint:[RLMSyncAuthEndpoint endpoint] + server:self.authServerURL JSON:json + timeout:60 + options:[[RLMSyncManager sharedManager] networkRequestOptions] completion:handler]; } diff --git a/Pods/Realm/Realm/RLMSyncSubscription.mm b/Pods/Realm/Realm/RLMSyncSubscription.mm new file mode 100644 index 0000000..61b12a2 --- /dev/null +++ b/Pods/Realm/Realm/RLMSyncSubscription.mm @@ -0,0 +1,105 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2018 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMSyncSubscription.h" + +#import "RLMRealm_Private.hpp" +#import "RLMResults_Private.hpp" +#import "RLMUtil.hpp" + +#import "sync/partial_sync.hpp" + +using namespace realm; + +@interface RLMSyncSubscription () +- (instancetype)initWithName:(NSString *)name results:(Results&)results realm:(RLMRealm *)realm; + +@property (nonatomic, readwrite) RLMSyncSubscriptionState state; +@property (nonatomic, readwrite, nullable) NSError *error; +@end + +@implementation RLMSyncSubscription { + partial_sync::SubscriptionNotificationToken _token; + util::Optional _subscription; + RLMRealm *_realm; +} + +- (instancetype)initWithName:(NSString *)name results:(Results&)results realm:(RLMRealm *)realm { + if (!(self = [super init])) + return nil; + + _name = [name copy]; + _realm = realm; + _subscription = partial_sync::subscribe(results, name ? util::make_optional(name.UTF8String) : util::none); + self.state = (RLMSyncSubscriptionState)_subscription->state(); + __weak RLMSyncSubscription *weakSelf = self; + _token = _subscription->add_notification_callback([weakSelf] { + RLMSyncSubscription *self = weakSelf; + if (!self) + return; + + // Retrieve the current error and status. Update our properties only if the values have changed, + // since clients use KVO to observe these properties. + + if (auto error = self->_subscription->error()) { + try { + std::rethrow_exception(error); + } catch (...) { + NSError *nsError; + RLMRealmTranslateException(&nsError); + if (!self.error || ![self.error isEqual:nsError]) + self.error = nsError; + } + } + else if (self.error != nil) + self.error = nil; + + auto status = (RLMSyncSubscriptionState)self->_subscription->state(); + if (status != self.state) + self.state = (RLMSyncSubscriptionState)status; + }); + + return self; +} + +- (void)unsubscribe { + partial_sync::unsubscribe(*_subscription); +} + +- (RLMResults *)results { + auto results = _subscription->results(); + return [RLMResults resultsWithObjectInfo:_realm->_info[RLMStringDataToNSString(results.get_object_type())] + results:std::move(results)]; +} +@end + +@implementation RLMResults (SyncSubscription) + +- (RLMSyncSubscription *)subscribe { + return [self _subscribeWithName:nil]; +} + +- (RLMSyncSubscription *)subscribeWithName:(NSString *)subscriptionName { + return [self _subscribeWithName:subscriptionName]; +} + +- (RLMSyncSubscription *)_subscribeWithName:(NSString *)subscriptionName { + return [[RLMSyncSubscription alloc] initWithName:subscriptionName results:_results realm:self.realm]; +} + +@end diff --git a/Pods/Realm/Realm/RLMSyncUser.mm b/Pods/Realm/Realm/RLMSyncUser.mm index 2214dc2..e6cdd82 100644 --- a/Pods/Realm/Realm/RLMSyncUser.mm +++ b/Pods/Realm/Realm/RLMSyncUser.mm @@ -18,12 +18,20 @@ #import "RLMSyncUser_Private.hpp" -#import "RLMAuthResponseModel.h" +#import "RLMJSONModels.h" #import "RLMNetworkClient.h" +#import "RLMRealmConfiguration+Sync.h" +#import "RLMRealmConfiguration_Private.hpp" +#import "RLMRealmUtil.hpp" +#import "RLMResults_Private.hpp" +#import "RLMSyncConfiguration.h" +#import "RLMSyncConfiguration_Private.hpp" #import "RLMSyncManager_Private.h" -#import "RLMSyncSession_Private.hpp" +#import "RLMSyncPermissionResults.h" +#import "RLMSyncPermission_Private.hpp" #import "RLMSyncSessionRefreshHandle.hpp" -#import "RLMTokenModels.h" +#import "RLMSyncSession_Private.hpp" +#import "RLMSyncUtil_Private.hpp" #import "RLMUtil.hpp" #import "sync/sync_manager.hpp" @@ -31,21 +39,99 @@ #import "sync/sync_user.hpp" using namespace realm; +using ConfigMaker = std::function, std::string)>; -@interface RLMSyncUser () { - std::shared_ptr _user; +namespace { + +std::function RLMWrapPermissionResultsCallback(RLMPermissionResultsBlock callback) { + return [callback](Results results, std::exception_ptr ptr) { + if (ptr) { + NSError *error = translateSyncExceptionPtrToError(std::move(ptr), RLMPermissionActionTypeGet); + REALM_ASSERT(error); + callback(nil, error); + } else { + // Finished successfully + callback([[RLMSyncPermissionResults alloc] initWithResults:std::move(results)], nil); + } + }; } -- (instancetype)initWithAuthServer:(nullable NSURL *)authServer NS_DESIGNATED_INITIALIZER; +NSString *tildeSubstitutedPathForRealmURL(NSURL *url, NSString *identity) { + return [[url path] stringByReplacingOccurrencesOfString:@"~" withString:identity]; +} -@property (nonatomic, readwrite) NSURL *authenticationServer; +} + +void CocoaSyncUserContext::register_refresh_handle(const std::string& path, RLMSyncSessionRefreshHandle *handle) +{ + REALM_ASSERT(handle); + std::lock_guard lock(m_mutex); + auto it = m_refresh_handles.find(path); + if (it != m_refresh_handles.end()) { + [it->second invalidate]; + m_refresh_handles.erase(it); + } + m_refresh_handles.insert({path, handle}); +} -/** - All 'refresh handles' associated with Realms opened by this user. A refresh handle is - an object that encapsulates the concept of periodically refreshing the Realm's access - token before it expires. Tokens are indexed by their paths (e.g. `/~/path/to/realm`). - */ -@property (nonatomic) NSMutableDictionary *refreshHandles; +void CocoaSyncUserContext::unregister_refresh_handle(const std::string& path) +{ + std::lock_guard lock(m_mutex); + m_refresh_handles.erase(path); +} + +void CocoaSyncUserContext::invalidate_all_handles() +{ + std::lock_guard lock(m_mutex); + for (auto& it : m_refresh_handles) { + [it.second invalidate]; + } + m_refresh_handles.clear(); +} + +RLMUserErrorReportingBlock CocoaSyncUserContext::error_handler() const +{ + std::lock_guard lock(m_error_handler_mutex); + return m_error_handler; +} + +void CocoaSyncUserContext::set_error_handler(RLMUserErrorReportingBlock block) +{ + std::lock_guard lock(m_error_handler_mutex); + m_error_handler = block; +} + +PermissionChangeCallback RLMWrapPermissionStatusCallback(RLMPermissionStatusBlock callback) { + return [callback](std::exception_ptr ptr) { + if (ptr) { + NSError *error = translateSyncExceptionPtrToError(std::move(ptr), RLMPermissionActionTypeChange); + REALM_ASSERT(error); + callback(error); + } else { + // Finished successfully + callback(nil); + } + }; +} + +@interface RLMSyncUserInfo () + +@property (nonatomic, readwrite) NSArray *accounts; +@property (nonatomic, readwrite) NSDictionary *metadata; +@property (nonatomic, readwrite) NSString *identity; +@property (nonatomic, readwrite) BOOL isAdmin; + ++ (instancetype)syncUserInfoWithModel:(RLMUserResponseModel *)model; + +@end + +@interface RLMSyncUser () { + std::shared_ptr _user; + // FIXME: remove this when the object store ConfigMaker goes away + std::unique_ptr _configMaker; +} + +- (instancetype)initPrivate NS_DESIGNATED_INITIALIZER; @end @@ -69,18 +155,20 @@ + (RLMSyncUser *)currentUser { #pragma mark - API -- (instancetype)initWithAuthServer:(nullable NSURL *)authServer { +- (instancetype)initPrivate { if (self = [super init]) { - self.authenticationServer = authServer; - self.refreshHandles = [NSMutableDictionary dictionary]; + _configMaker = std::make_unique([](std::shared_ptr user, std::string url) { + NSURL *objCUrl = [NSURL URLWithString:@(url.c_str())]; + RLMSyncUser *objCUser = [[RLMSyncUser alloc] initWithSyncUser:std::move(user)]; + return [objCUser configurationWithURL:objCUrl fullSynchronization:true].config; + }); return self; } return nil; } - (instancetype)initWithSyncUser:(std::shared_ptr)user { - NSString *rawServerURL = @(user->server_url().c_str()); - if (self = [self initWithAuthServer:[NSURL URLWithString:rawServerURL]]) { + if (self = [self initPrivate]) { _user = user; return self; } @@ -100,37 +188,89 @@ + (void)logInWithCredentials:(RLMSyncCredentials *)credential [self logInWithCredentials:credential authServerURL:authServerURL timeout:30 + callbackQueue:dispatch_get_main_queue() onCompletion:completion]; } + (void)logInWithCredentials:(RLMSyncCredentials *)credential authServerURL:(NSURL *)authServerURL timeout:(NSTimeInterval)timeout + callbackQueue:(dispatch_queue_t)callbackQueue onCompletion:(RLMUserCompletionBlock)completion { - RLMSyncUser *user = [[RLMSyncUser alloc] initWithAuthServer:authServerURL]; + RLMSyncUser *user = [[RLMSyncUser alloc] initPrivate]; [RLMSyncUser _performLogInForUser:user credentials:credential authServerURL:authServerURL timeout:timeout + callbackQueue:callbackQueue completionBlock:completion]; } +- (RLMRealmConfiguration *)configuration { + return [self configurationWithURL:nil + fullSynchronization:NO + enableSSLValidation:YES + urlPrefix:nil]; +} + +- (RLMRealmConfiguration *)configurationWithURL:(NSURL *)url { + return [self configurationWithURL:url + fullSynchronization:NO + enableSSLValidation:YES + urlPrefix:nil]; +} + +- (RLMRealmConfiguration *)configurationWithURL:(NSURL *)url fullSynchronization:(bool)fullSynchronization { + return [self configurationWithURL:url + fullSynchronization:fullSynchronization + enableSSLValidation:YES + urlPrefix:nil]; +} + +- (RLMRealmConfiguration *)configurationWithURL:(NSURL *)url + fullSynchronization:(bool)fullSynchronization + enableSSLValidation:(bool)enableSSLValidation + urlPrefix:(NSString * _Nullable)urlPrefix { + auto syncConfig = [[RLMSyncConfiguration alloc] initWithUser:self + realmURL:url ?: self.defaultRealmURL + customFileURL:nil + isPartial:!fullSynchronization + stopPolicy:RLMSyncStopPolicyAfterChangesUploaded + errorHandler:nullptr]; + syncConfig.urlPrefix = urlPrefix; + syncConfig.enableSSLValidation = enableSSLValidation; + RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init]; + config.syncConfiguration = syncConfig; + return config; +} + - (void)logOut { if (!_user) { return; } _user->log_out(); - for (id key in self.refreshHandles) { - [self.refreshHandles[key] invalidate]; + context_for(_user).invalidate_all_handles(); +} + +- (RLMUserErrorReportingBlock)errorHandler { + if (!_user) { + return nil; + } + return context_for(_user).error_handler(); +} + +- (void)setErrorHandler:(RLMUserErrorReportingBlock)errorHandler { + if (!_user) { + return; } - [self.refreshHandles removeAllObjects]; + context_for(_user).set_error_handler([errorHandler copy]); } - (nullable RLMSyncSession *)sessionForURL:(NSURL *)url { if (!_user) { return nil; } - auto path = SyncManager::shared().path_for_realm(_user->identity(), [url.absoluteString UTF8String]); + auto path = SyncManager::shared().path_for_realm(*_user, [url.absoluteString UTF8String]); if (auto session = _user->session_for_on_disk_path(path)) { return [[RLMSyncSession alloc] initWithSyncSession:session]; } @@ -170,14 +310,227 @@ - (RLMSyncUserState)state { } } -- (RLMRealm *)managementRealmWithError:(NSError **)error { - return [RLMRealm realmWithConfiguration:[RLMRealmConfiguration managementConfigurationForUser:self] error:error]; +- (NSURL *)authenticationServer { + if (!_user || _user->token_type() == SyncUser::TokenType::Admin) { + return nil; + } + return [NSURL URLWithString:@(_user->server_url().c_str())]; +} + +- (BOOL)isAdmin { + if (!_user) { + return NO; + } + return _user->is_admin(); +} + +#pragma mark - Passwords + +- (void)changePassword:(NSString *)newPassword completion:(RLMPasswordChangeStatusBlock)completion { + [self changePassword:newPassword forUserID:self.identity completion:completion]; +} + +- (void)changePassword:(NSString *)newPassword forUserID:(NSString *)userID completion:(RLMPasswordChangeStatusBlock)completion { + if (self.state != RLMSyncUserStateActive) { + completion([NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorClientSessionError + userInfo:nil]); + return; + } + [RLMSyncChangePasswordEndpoint sendRequestToServer:self.authenticationServer + JSON:@{kRLMSyncTokenKey: self._refreshToken, + kRLMSyncUserIDKey: userID, + kRLMSyncDataKey: @{kRLMSyncNewPasswordKey: newPassword}} + options:[[RLMSyncManager sharedManager] networkRequestOptions] + completion:completion]; +} + ++ (void)requestPasswordResetForAuthServer:(NSURL *)serverURL + userEmail:(NSString *)email + completion:(RLMPasswordChangeStatusBlock)completion { + [RLMSyncUpdateAccountEndpoint sendRequestToServer:serverURL + JSON:@{@"provider_id": email, @"data": @{@"action": @"reset_password"}} + options:[[RLMSyncManager sharedManager] networkRequestOptions] + completion:completion]; +} + ++ (void)completePasswordResetForAuthServer:(NSURL *)serverURL + token:(NSString *)token + password:(NSString *)newPassword + completion:(RLMPasswordChangeStatusBlock)completion { + [RLMSyncUpdateAccountEndpoint sendRequestToServer:serverURL + JSON:@{@"data": @{@"action": @"complete_reset", + @"token": token, + @"new_password": newPassword}} + options:[[RLMSyncManager sharedManager] networkRequestOptions] + completion:completion]; +} + ++ (void)requestEmailConfirmationForAuthServer:(NSURL *)serverURL + userEmail:(NSString *)email + completion:(RLMPasswordChangeStatusBlock)completion { + [RLMSyncUpdateAccountEndpoint sendRequestToServer:serverURL + JSON:@{@"data": @{@"provider_id": email, + @"action": @"request_email_confirmation"}} + options:[[RLMSyncManager sharedManager] networkRequestOptions] + completion:completion]; +} + ++ (void)confirmEmailForAuthServer:(NSURL *)serverURL + token:(NSString *)token + completion:(RLMPasswordChangeStatusBlock)completion { + [RLMSyncUpdateAccountEndpoint sendRequestToServer:serverURL + JSON:@{@"data": @{@"action": @"confirm_email", + @"token": token}} + options:[[RLMSyncManager sharedManager] networkRequestOptions] + completion:completion]; +} + +#pragma mark - Administrator API + +- (void)retrieveInfoForUser:(NSString *)providerUserIdentity + identityProvider:(RLMIdentityProvider)provider + completion:(RLMRetrieveUserBlock)completion { + [RLMNetworkClient sendRequestToEndpoint:[RLMSyncGetUserInfoEndpoint endpoint] + server:self.authenticationServer + JSON:@{ + kRLMSyncProviderKey: provider, + kRLMSyncProviderIDKey: providerUserIdentity, + kRLMSyncTokenKey: self._refreshToken + } + timeout:60 + options:[[RLMSyncManager sharedManager] networkRequestOptions] + completion:^(NSError *error, NSDictionary *json) { + if (error) { + completion(nil, error); + return; + } + RLMUserResponseModel *model = [[RLMUserResponseModel alloc] initWithDictionary:json]; + if (!model) { + completion(nil, make_auth_error_bad_response(json)); + return; + } + completion([RLMSyncUserInfo syncUserInfoWithModel:model], nil); + }]; +} + +#pragma mark - Permissions API + +static void verifyInRunLoop() { + if (!RLMIsInRunLoop()) { + @throw RLMException(@"Can only access or modify permissions from a thread which has a run loop (by default, only the main thread)."); + } +} + +- (void)retrievePermissionsWithCallback:(RLMPermissionResultsBlock)callback { + verifyInRunLoop(); + if (!_user || _user->state() == SyncUser::State::Error) { + callback(nullptr, make_permission_error_get(@"Permissions cannot be retrieved using an invalid user.")); + return; + } + Permissions::get_permissions(_user, RLMWrapPermissionResultsCallback(callback), *_configMaker); +} + +- (void)applyPermission:(RLMSyncPermission *)permission callback:(RLMPermissionStatusBlock)callback { + verifyInRunLoop(); + if (!_user || _user->state() == SyncUser::State::Error) { + callback(make_permission_error_change(@"Permissions cannot be applied using an invalid user.")); + return; + } + Permissions::set_permission(_user, + [permission rawPermission], + RLMWrapPermissionStatusCallback(callback), + *_configMaker); +} + +- (void)revokePermission:(RLMSyncPermission *)permission callback:(RLMPermissionStatusBlock)callback { + verifyInRunLoop(); + if (!_user || _user->state() == SyncUser::State::Error) { + callback(make_permission_error_change(@"Permissions cannot be revoked using an invalid user.")); + return; + } + Permissions::delete_permission(_user, + [permission rawPermission], + RLMWrapPermissionStatusCallback(callback), + *_configMaker); +} + +- (void)createOfferForRealmAtURL:(NSURL *)url + accessLevel:(RLMSyncAccessLevel)accessLevel + expiration:(NSDate *)expirationDate + callback:(RLMPermissionOfferStatusBlock)callback { + verifyInRunLoop(); + if (!_user || _user->state() == SyncUser::State::Error) { + callback(nil, make_permission_error_change(@"A permission offer cannot be created using an invalid user.")); + return; + } + auto cb = [callback](util::Optional token, std::exception_ptr ptr) { + if (ptr) { + NSError *error = translateSyncExceptionPtrToError(std::move(ptr), RLMPermissionActionTypeOffer); + REALM_ASSERT_DEBUG(error); + callback(nil, error); + } else { + REALM_ASSERT_DEBUG(token); + callback(@(token->c_str()), nil); + } + }; + auto offer = PermissionOffer{ + [tildeSubstitutedPathForRealmURL(url, self.identity) UTF8String], + accessLevelForObjCAccessLevel(accessLevel), + RLMTimestampForNSDate(expirationDate), + }; + Permissions::make_offer(_user, std::move(offer), std::move(cb), *_configMaker); +} + +- (void)acceptOfferForToken:(NSString *)token + callback:(RLMPermissionOfferResponseStatusBlock)callback { + verifyInRunLoop(); + if (!_user || _user->state() == SyncUser::State::Error) { + callback(nil, make_permission_error_change(@"A permission offer cannot be accepted by an invalid user.")); + return; + } + NSURLComponents *baseURL = [NSURLComponents componentsWithURL:self.authenticationServer + resolvingAgainstBaseURL:YES]; + if ([baseURL.scheme isEqualToString:@"http"]) { + baseURL.scheme = @"realm"; + } else if ([baseURL.scheme isEqualToString:@"https"]) { + baseURL.scheme = @"realms"; + } + auto cb = [baseURL, callback](util::Optional raw_path, std::exception_ptr ptr) { + if (ptr) { + NSError *error = translateSyncExceptionPtrToError(std::move(ptr), RLMPermissionActionTypeAcceptOffer); + REALM_ASSERT_DEBUG(error); + callback(nil, error); + } else { + // Note that ROS currently vends the path to the Realm, so we need to construct the full URL ourselves. + REALM_ASSERT_DEBUG(raw_path); + baseURL.path = @(raw_path->c_str()); + callback([baseURL URL], nil); + } + }; + Permissions::accept_offer(_user, [token UTF8String], std::move(cb), *_configMaker); } #pragma mark - Private API -- (void)_unregisterRefreshHandleForURLPath:(NSString *)path { - [self.refreshHandles removeObjectForKey:path]; +- (NSURL *)defaultRealmURL +{ + NSURLComponents *components = [NSURLComponents componentsWithURL:self.authenticationServer resolvingAgainstBaseURL:YES]; + if ([components.scheme caseInsensitiveCompare:@"http"] == NSOrderedSame) + components.scheme = @"realm"; + else if ([components.scheme caseInsensitiveCompare:@"https"] == NSOrderedSame) + components.scheme = @"realms"; + else + @throw RLMException(@"The provided user's authentication server URL (%@) was not valid.", self.authenticationServer); + + components.path = @"/default"; + return components.URL; +} + ++ (void)_setUpBindingContextFactory { + SyncUser::set_binding_context_factory([] { + return std::make_shared(); + }); } - (NSString *)_refreshToken { @@ -187,20 +540,6 @@ - (NSString *)_refreshToken { return @(_user->refresh_token().c_str()); } -- (void)_bindSessionWithConfig:(const SyncConfig&)config - session:(std::shared_ptr)session - completion:(RLMSyncBasicErrorReportingBlock)completion { - // Create a refresh handle, and have it handle all the work. - NSURL *realmURL = [NSURL URLWithString:@(config.realm_url.c_str())]; - NSString *path = [realmURL path]; - REALM_ASSERT(realmURL && path); - [self.refreshHandles[path] invalidate]; - self.refreshHandles[path] = [[RLMSyncSessionRefreshHandle alloc] initWithRealmURL:realmURL - user:self - session:std::move(session) - completionBlock:completion]; -} - - (std::shared_ptr)_syncUser { return _user; } @@ -209,12 +548,19 @@ + (void)_performLogInForUser:(RLMSyncUser *)user credentials:(RLMSyncCredentials *)credentials authServerURL:(NSURL *)authServerURL timeout:(NSTimeInterval)timeout + callbackQueue:(dispatch_queue_t)callbackQueue completionBlock:(RLMUserCompletionBlock)completion { // Special credential login should be treated differently. if (credentials.provider == RLMIdentityProviderAccessToken) { - [self _performLoginForDirectAccessTokenCredentials:credentials user:user completionBlock:completion]; + [self _performLoginForDirectAccessTokenCredentials:credentials + user:user + authServerURL:authServerURL + completionBlock:completion]; return; } + if (!authServerURL) { + @throw RLMException(@"A user cannot be logged in without specifying an authentication server URL."); + } // Prepare login network request NSMutableDictionary *json = [@{ @@ -236,47 +582,79 @@ + (void)_performLogInForUser:(RLMSyncUser *)user requireRefreshToken:YES]; if (!model) { // Malformed JSON - error = [NSError errorWithDomain:RLMSyncErrorDomain - code:RLMSyncErrorBadResponse - userInfo:@{kRLMSyncErrorJSONKey: json}]; - completion(nil, error); + NSError *badResponseError = make_auth_error_bad_response(json); + dispatch_async(callbackQueue, ^{ + completion(nil, badResponseError); + }); return; } else { std::string server_url = authServerURL.absoluteString.UTF8String; - auto sync_user = SyncManager::shared().get_user([model.refreshToken.tokenData.identity UTF8String], - [model.refreshToken.token UTF8String], - std::move(server_url)); + SyncUserIdentifier identity{[model.refreshToken.tokenData.identity UTF8String], std::move(server_url)}; + auto sync_user = SyncManager::shared().get_user(identity , [model.refreshToken.token UTF8String]); if (!sync_user) { - completion(nil, [NSError errorWithDomain:RLMSyncErrorDomain - code:RLMSyncErrorClientSessionError - userInfo:nil]); + NSError *authError = make_auth_error_client_issue(); + dispatch_async(callbackQueue, ^{ + completion(nil, authError); + }); return; } + sync_user->set_is_admin(model.refreshToken.tokenData.isAdmin); user->_user = sync_user; - completion(user, nil); + dispatch_async(callbackQueue, ^{ + completion(user, nil); + }); } } else { // Something else went wrong - completion(nil, error); + dispatch_async(callbackQueue, ^{ + completion(nil, error); + }); } }; - [RLMNetworkClient postRequestToEndpoint:RLMServerEndpointAuth + + [RLMNetworkClient sendRequestToEndpoint:[RLMSyncAuthEndpoint endpoint] server:authServerURL JSON:json timeout:timeout - completion:handler]; + options:[[RLMSyncManager sharedManager] networkRequestOptions] + completion:^(NSError *error, NSDictionary *dictionary) { + dispatch_async(callbackQueue, ^{ + handler(error, dictionary); + }); + }]; } + (void)_performLoginForDirectAccessTokenCredentials:(RLMSyncCredentials *)credentials user:(RLMSyncUser *)user + authServerURL:(NSURL *)serverURL completionBlock:(nonnull RLMUserCompletionBlock)completion { NSString *identity = credentials.userInfo[kRLMSyncIdentityKey]; - NSAssert(identity != nil, @"Improperly created direct access token credential."); - auto sync_user = SyncManager::shared().get_user([identity UTF8String], [credentials.token UTF8String], none, true); + std::shared_ptr sync_user; + if (serverURL) { + NSString *scheme = serverURL.scheme; + if (![scheme isEqualToString:@"http"] && ![scheme isEqualToString:@"https"]) { + @throw RLMException(@"The Realm Object Server authentication URL provided for this user, \"%@\", " + @" is invalid. It must begin with http:// or https://.", serverURL); + } + // Retrieve the user based on the auth server URL. + util::Optional identity_string; + if (identity) { + identity_string = std::string(identity.UTF8String); + } + sync_user = SyncManager::shared().get_admin_token_user([serverURL absoluteString].UTF8String, + credentials.token.UTF8String, + std::move(identity_string)); + } else { + // Retrieve the user based on the identity. + if (!identity) { + @throw RLMException(@"A direct access credential must specify either an identity, a server URL, or both."); + } + sync_user = SyncManager::shared().get_admin_token_user_from_identity(identity.UTF8String, + none, + credentials.token.UTF8String); + } if (!sync_user) { - completion(nil, [NSError errorWithDomain:RLMSyncErrorDomain - code:RLMSyncErrorClientSessionError - userInfo:nil]); + completion(nil, make_auth_error_client_issue()); return; } user->_user = sync_user; @@ -284,3 +662,22 @@ + (void)_performLoginForDirectAccessTokenCredentials:(RLMSyncCredentials *)crede } @end + +#pragma mark - RLMSyncUserInfo + +@implementation RLMSyncUserInfo + +- (instancetype)initPrivate { + return [super init]; +} + ++ (instancetype)syncUserInfoWithModel:(RLMUserResponseModel *)model { + RLMSyncUserInfo *info = [[RLMSyncUserInfo alloc] initPrivate]; + info.accounts = model.accounts; + info.metadata = model.metadata; + info.isAdmin = model.isAdmin; + info.identity = model.identity; + return info; +} + +@end diff --git a/Pods/Realm/Realm/RLMSyncUtil.mm b/Pods/Realm/Realm/RLMSyncUtil.mm index c3f9f60..46c90c5 100644 --- a/Pods/Realm/Realm/RLMSyncUtil.mm +++ b/Pods/Realm/Realm/RLMSyncUtil.mm @@ -16,53 +16,77 @@ // //////////////////////////////////////////////////////////////////////////// -#import - #import "RLMSyncUtil_Private.hpp" -#import "RLMSyncUser_Private.hpp" + +#import "RLMJSONModels.h" +#import "RLMObject_Private.hpp" #import "RLMRealmConfiguration+Sync.h" #import "RLMRealmConfiguration_Private.hpp" -#import "RLMSyncPermissionChange.h" -#import "RLMSyncPermissionOffer.h" -#import "RLMSyncPermissionOfferResponse.h" - -@implementation RLMRealmConfiguration (RealmSync) -+ (instancetype)managementConfigurationForUser:(RLMSyncUser *)user { - NSURLComponents *components = [NSURLComponents componentsWithURL:user.authenticationServer resolvingAgainstBaseURL:NO]; - if ([components.scheme isEqualToString:@"https"]) { - components.scheme = @"realms"; - } else { - components.scheme = @"realm"; - } - components.path = @"/~/__management"; - NSURL *managementRealmURL = components.URL; - RLMSyncConfiguration *syncConfig = [[RLMSyncConfiguration alloc] initWithUser:user realmURL:managementRealmURL]; - RLMRealmConfiguration *config = [RLMRealmConfiguration new]; - config.syncConfiguration = syncConfig; - config.objectClasses = @[RLMSyncPermissionChange.class, RLMSyncPermissionOffer.class, RLMSyncPermissionOfferResponse.class]; - return config; -} -@end +#import "RLMRealm_Private.hpp" +#import "RLMSyncConfiguration_Private.hpp" +#import "RLMSyncUser_Private.hpp" +#import "RLMUtil.hpp" + +#import "shared_realm.hpp" + +#import "sync/sync_permission.hpp" +#import "sync/sync_user.hpp" RLMIdentityProvider const RLMIdentityProviderAccessToken = @"_access_token"; NSString *const RLMSyncErrorDomain = @"io.realm.sync"; +NSString *const RLMSyncAuthErrorDomain = @"io.realm.sync.auth"; +NSString *const RLMSyncPermissionErrorDomain = @"io.realm.sync.permission"; NSString *const kRLMSyncPathOfRealmBackupCopyKey = @"recovered_realm_location_path"; -NSString *const kRLMSyncInitiateClientResetBlockKey = @"initiate_client_reset_block"; +NSString *const kRLMSyncErrorActionTokenKey = @"error_action_token"; NSString *const kRLMSyncAppIDKey = @"app_id"; NSString *const kRLMSyncDataKey = @"data"; NSString *const kRLMSyncErrorJSONKey = @"json"; NSString *const kRLMSyncErrorStatusCodeKey = @"statusCode"; NSString *const kRLMSyncIdentityKey = @"identity"; +NSString *const kRLMSyncIsAdminKey = @"is_admin"; +NSString *const kRLMSyncNewPasswordKey = @"new_password"; NSString *const kRLMSyncPasswordKey = @"password"; NSString *const kRLMSyncPathKey = @"path"; NSString *const kRLMSyncProviderKey = @"provider"; +NSString *const kRLMSyncProviderIDKey = @"provider_id"; NSString *const kRLMSyncRegisterKey = @"register"; +NSString *const kRLMSyncTokenKey = @"token"; NSString *const kRLMSyncUnderlyingErrorKey = @"underlying_error"; +NSString *const kRLMSyncUserIDKey = @"user_id"; -namespace realm { +uint8_t RLMGetComputedPermissions(RLMRealm *realm, id _Nullable object) { + if (!object) { + return static_cast(realm->_realm->get_privileges()); + } + if ([object isKindOfClass:[NSString class]]) { + return static_cast(realm->_realm->get_privileges([object UTF8String])); + } + if (auto obj = RLMDynamicCast(object)) { + RLMVerifyAttached(obj); + return static_cast(realm->_realm->get_privileges(obj->_row)); + } + return 0; +} + +#pragma mark - C++ APIs + +namespace { + +NSError *make_permission_error(NSString *description, util::Optional code, RLMSyncPermissionError type) { + NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; + if (description) { + userInfo[NSLocalizedDescriptionKey] = description; + } + if (code) { + userInfo[kRLMSyncErrorStatusCodeKey] = @(*code); + } + return [NSError errorWithDomain:RLMSyncPermissionErrorDomain code:type userInfo:userInfo]; +} + +} SyncSessionStopPolicy translateStopPolicy(RLMSyncStopPolicy stopPolicy) { switch (stopPolicy) { @@ -73,8 +97,7 @@ SyncSessionStopPolicy translateStopPolicy(RLMSyncStopPolicy stopPolicy) { REALM_UNREACHABLE(); // Unrecognized stop policy. } -RLMSyncStopPolicy translateStopPolicy(SyncSessionStopPolicy stop_policy) -{ +RLMSyncStopPolicy translateStopPolicy(SyncSessionStopPolicy stop_policy) { switch (stop_policy) { case SyncSessionStopPolicy::Immediately: return RLMSyncStopPolicyImmediately; case SyncSessionStopPolicy::LiveIndefinitely: return RLMSyncStopPolicyLiveIndefinitely; @@ -83,14 +106,164 @@ RLMSyncStopPolicy translateStopPolicy(SyncSessionStopPolicy stop_policy) REALM_UNREACHABLE(); } +NSError *translateSyncExceptionPtrToError(std::exception_ptr ptr, RLMPermissionActionType type) { + NSError *error = nil; + try { + std::rethrow_exception(ptr); + } catch (PermissionActionException const& ex) { + switch (type) { + case RLMPermissionActionTypeGet: + error = make_permission_error_get(@(ex.what()), ex.code); + break; + case RLMPermissionActionTypeChange: + error = make_permission_error_change(@(ex.what()), ex.code); + break; + case RLMPermissionActionTypeOffer: + error = make_permission_error_offer(@(ex.what()), ex.code); + break; + case RLMPermissionActionTypeAcceptOffer: + error = make_permission_error_accept_offer(@(ex.what()), ex.code); + break; + } + } + catch (const std::exception &exp) { + RLMSetErrorOrThrow(RLMMakeError(RLMErrorFail, exp), &error); + } + return error; +} + +std::shared_ptr sync_session_for_realm(RLMRealm *realm) { + Realm::Config realmConfig = realm.configuration.config; + if (auto config = realmConfig.sync_config) { + std::shared_ptr user = config->user; + if (user && user->state() != SyncUser::State::Error) { + return user->session_for_on_disk_path(realmConfig.path); + } + } + return nullptr; +} + +CocoaSyncUserContext& context_for(const std::shared_ptr& user) +{ + return *std::static_pointer_cast(user->binding_context()); +} + +AccessLevel accessLevelForObjCAccessLevel(RLMSyncAccessLevel level) { + switch (level) { + case RLMSyncAccessLevelNone: + return AccessLevel::None; + case RLMSyncAccessLevelRead: + return AccessLevel::Read; + case RLMSyncAccessLevelWrite: + return AccessLevel::Write; + case RLMSyncAccessLevelAdmin: + return AccessLevel::Admin; + } + REALM_UNREACHABLE(); +} + +RLMSyncAccessLevel objCAccessLevelForAccessLevel(AccessLevel level) { + switch (level) { + case AccessLevel::None: + return RLMSyncAccessLevelNone; + case AccessLevel::Read: + return RLMSyncAccessLevelRead; + case AccessLevel::Write: + return RLMSyncAccessLevelWrite; + case AccessLevel::Admin: + return RLMSyncAccessLevelAdmin; + } + REALM_UNREACHABLE(); +} + +NSError *make_auth_error_bad_response(NSDictionary *json) { + return [NSError errorWithDomain:RLMSyncAuthErrorDomain + code:RLMSyncAuthErrorBadResponse + userInfo:json ? @{kRLMSyncErrorJSONKey: json} : nil]; } -RLMSyncManagementObjectStatus RLMMakeSyncManagementObjectStatus(NSNumber *statusCode) { - if (!statusCode) { - return RLMSyncManagementObjectStatusNotProcessed; +NSError *make_auth_error_http_status(NSInteger status) { + return [NSError errorWithDomain:RLMSyncAuthErrorDomain + code:RLMSyncAuthErrorHTTPStatusCodeError + userInfo:@{kRLMSyncErrorStatusCodeKey: @(status)}]; +} + +NSError *make_auth_error_client_issue() { + return [NSError errorWithDomain:RLMSyncAuthErrorDomain + code:RLMSyncAuthErrorClientSessionError + userInfo:nil]; +} + +NSError *make_auth_error(RLMSyncErrorResponseModel *model) { + NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithCapacity:2]; + if (NSString *description = model.title) { + [userInfo setObject:description forKey:NSLocalizedDescriptionKey]; } - if (statusCode.integerValue == 0) { - return RLMSyncManagementObjectStatusSuccess; + if (NSString *hint = model.hint) { + [userInfo setObject:hint forKey:NSLocalizedRecoverySuggestionErrorKey]; } - return RLMSyncManagementObjectStatusError; + return [NSError errorWithDomain:RLMSyncAuthErrorDomain code:model.code userInfo:userInfo]; +} + +NSError *make_permission_error_get(NSString *description, util::Optional code) { + return make_permission_error(description, std::move(code), RLMSyncPermissionErrorGetFailed); +} + +NSError *make_permission_error_change(NSString *description, util::Optional code) { + return make_permission_error(description, std::move(code), RLMSyncPermissionErrorChangeFailed); +} + +NSError *make_permission_error_offer(NSString *description, util::Optional code) { + return make_permission_error(description, std::move(code), RLMSyncPermissionErrorOfferFailed); +} + +NSError *make_permission_error_accept_offer(NSString *description, util::Optional code) { + return make_permission_error(description, std::move(code), RLMSyncPermissionErrorAcceptOfferFailed); +} + +NSError *make_sync_error(RLMSyncSystemErrorKind kind, NSString *description, NSInteger code, NSDictionary *custom) { + NSMutableDictionary *buffer = [custom ?: @{} mutableCopy]; + buffer[NSLocalizedDescriptionKey] = description; + if (code != NSNotFound) { + buffer[kRLMSyncErrorStatusCodeKey] = @(code); + } + + RLMSyncError errorCode; + switch (kind) { + case RLMSyncSystemErrorKindClientReset: + errorCode = RLMSyncErrorClientResetError; + break; + case RLMSyncSystemErrorKindPermissionDenied: + errorCode = RLMSyncErrorPermissionDeniedError; + break; + case RLMSyncSystemErrorKindUser: + errorCode = RLMSyncErrorClientUserError; + break; + case RLMSyncSystemErrorKindSession: + errorCode = RLMSyncErrorClientSessionError; + break; + case RLMSyncSystemErrorKindConnection: + case RLMSyncSystemErrorKindClient: + case RLMSyncSystemErrorKindUnknown: + errorCode = RLMSyncErrorClientInternalError; + break; + } + return [NSError errorWithDomain:RLMSyncErrorDomain + code:errorCode + userInfo:[buffer copy]]; +} + +NSError *make_sync_error(NSError *wrapped_auth_error) { + return [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorUnderlyingAuthError + userInfo:@{kRLMSyncUnderlyingErrorKey: wrapped_auth_error}]; +} + +NSError *make_sync_error(std::error_code sync_error, RLMSyncSystemErrorKind kind) { + return [NSError errorWithDomain:RLMSyncErrorDomain + code:kind + userInfo:@{ + NSLocalizedDescriptionKey: @(sync_error.message().c_str()), + kRLMSyncErrorStatusCodeKey: @(sync_error.value()) + }]; } diff --git a/Pods/Realm/Realm/RLMTokenModels.m b/Pods/Realm/Realm/RLMTokenModels.m deleted file mode 100644 index e85e70e..0000000 --- a/Pods/Realm/Realm/RLMTokenModels.m +++ /dev/null @@ -1,71 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#import "RLMTokenModels.h" -#import "RLMSyncUtil_Private.h" - -static const NSString *const kRLMSyncTokenDataKey = @"token_data"; -static const NSString *const kRLMSyncTokenKey = @"token"; -static const NSString *const kRLMSyncExpiresKey = @"expires"; - -@interface RLMTokenDataModel () - -@property (nonatomic, readwrite) NSString *identity; -@property (nonatomic, readwrite) NSString *appID; -@property (nonatomic, readwrite) NSString *path; -@property (nonatomic, readwrite) NSTimeInterval expires; -//@property (nonatomic, readwrite) NSArray *access; - -@end - -@implementation RLMTokenDataModel - -- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary { - if (self = [super init]) { - RLM_SYNC_PARSE_STRING_OR_ABORT(jsonDictionary, kRLMSyncIdentityKey, identity); - RLM_SYNC_PARSE_OPTIONAL_STRING(jsonDictionary, kRLMSyncAppIDKey, appID); - RLM_SYNC_PARSE_OPTIONAL_STRING(jsonDictionary, kRLMSyncPathKey, path); - RLM_SYNC_PARSE_DOUBLE_OR_ABORT(jsonDictionary, kRLMSyncExpiresKey, expires); - return self; - } - return nil; -} - -@end - -@interface RLMTokenModel () - -@property (nonatomic, readwrite) NSString *token; -@property (nonatomic, nullable, readwrite) NSString *path; -@property (nonatomic, readwrite) RLMTokenDataModel *tokenData; - -@end - -@implementation RLMTokenModel - -- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary { - if (self = [super init]) { - RLM_SYNC_PARSE_STRING_OR_ABORT(jsonDictionary, kRLMSyncTokenKey, token); - RLM_SYNC_PARSE_OPTIONAL_STRING(jsonDictionary, kRLMSyncPathKey, path); - RLM_SYNC_PARSE_MODEL_OR_ABORT(jsonDictionary, kRLMSyncTokenDataKey, RLMTokenDataModel, tokenData); - return self; - } - return nil; -} - -@end diff --git a/Pods/Realm/Realm/RLMUpdateChecker.mm b/Pods/Realm/Realm/RLMUpdateChecker.mm index e282ee8..4ff0dd6 100644 --- a/Pods/Realm/Realm/RLMUpdateChecker.mm +++ b/Pods/Realm/Realm/RLMUpdateChecker.mm @@ -31,6 +31,18 @@ void RLMCheckForUpdates() { return; } + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"alpha|beta|rc" + options:(NSRegularExpressionOptions)0 + error:nil]; + NSUInteger numberOfMatches = [regex numberOfMatchesInString:REALM_COCOA_VERSION + options:(NSMatchingOptions)0 + range:NSMakeRange(0, REALM_COCOA_VERSION.length)]; + + if (numberOfMatches > 0) { + // pre-release version, skip update checking + return; + } + auto handler = ^(NSData *data, NSURLResponse *response, NSError *error) { if (error || ((NSHTTPURLResponse *)response).statusCode != 200) { return; diff --git a/Pods/Realm/Realm/RLMUtil.mm b/Pods/Realm/Realm/RLMUtil.mm index fa77a9d..1298602 100644 --- a/Pods/Realm/Realm/RLMUtil.mm +++ b/Pods/Realm/Realm/RLMUtil.mm @@ -39,8 +39,7 @@ #import "RLMVersion.h" #endif -static inline bool nsnumber_is_like_integer(__unsafe_unretained NSNumber *const obj) -{ +static inline bool numberIsInteger(__unsafe_unretained NSNumber *const obj) { char data_type = [obj objCType][0]; return data_type == *@encode(bool) || data_type == *@encode(char) || @@ -54,15 +53,14 @@ static inline bool nsnumber_is_like_integer(__unsafe_unretained NSNumber *const data_type == *@encode(unsigned long long); } -static inline bool nsnumber_is_like_bool(__unsafe_unretained NSNumber *const obj) -{ +static inline bool numberIsBool(__unsafe_unretained NSNumber *const obj) { // @encode(BOOL) is 'B' on iOS 64 and 'c' // objcType is always 'c'. Therefore compare to "c". if ([obj objCType][0] == 'c') { return true; } - if (nsnumber_is_like_integer(obj)) { + if (numberIsInteger(obj)) { int value = [obj intValue]; return value == 0 || value == 1; } @@ -70,8 +68,7 @@ static inline bool nsnumber_is_like_bool(__unsafe_unretained NSNumber *const obj return false; } -static inline bool nsnumber_is_like_float(__unsafe_unretained NSNumber *const obj) -{ +static inline bool numberIsFloat(__unsafe_unretained NSNumber *const obj) { char data_type = [obj objCType][0]; return data_type == *@encode(float) || data_type == *@encode(short) || @@ -82,12 +79,11 @@ static inline bool nsnumber_is_like_float(__unsafe_unretained NSNumber *const ob data_type == *@encode(unsigned int) || data_type == *@encode(unsigned long) || data_type == *@encode(unsigned long long) || - // A double is like float if it fits within float bounds - (data_type == *@encode(double) && ABS([obj doubleValue]) <= FLT_MAX); + // A double is like float if it fits within float bounds or is NaN. + (data_type == *@encode(double) && (ABS([obj doubleValue]) <= FLT_MAX || isnan([obj doubleValue]))); } -static inline bool nsnumber_is_like_double(__unsafe_unretained NSNumber *const obj) -{ +static inline bool numberIsDouble(__unsafe_unretained NSNumber *const obj) { char data_type = [obj objCType][0]; return data_type == *@encode(double) || data_type == *@encode(float) || @@ -101,72 +97,149 @@ static inline bool nsnumber_is_like_double(__unsafe_unretained NSNumber *const o data_type == *@encode(unsigned long long); } -BOOL RLMIsObjectValidForProperty(__unsafe_unretained id const obj, - __unsafe_unretained RLMProperty *const property) { - if (property.optional && !RLMCoerceToNil(obj)) { +static inline RLMArray *asRLMArray(__unsafe_unretained id const value) { + return RLMDynamicCast(value) ?: RLMDynamicCast(value)._rlmArray; +} + +static inline bool checkArrayType(__unsafe_unretained RLMArray *const array, + RLMPropertyType type, bool optional, + __unsafe_unretained NSString *const objectClassName) { + return array.type == type && array.optional == optional + && (type != RLMPropertyTypeObject || [array.objectClassName isEqualToString:objectClassName]); +} + +BOOL RLMValidateValue(__unsafe_unretained id const value, + RLMPropertyType type, bool optional, bool array, + __unsafe_unretained NSString *const objectClassName) { + if (optional && !RLMCoerceToNil(value)) { return YES; } + if (array) { + if (auto rlmArray = asRLMArray(value)) { + return checkArrayType(rlmArray, type, optional, objectClassName); + } + if ([value conformsToProtocol:@protocol(NSFastEnumeration)]) { + // check each element for compliance + for (id el in (id)value) { + if (!RLMValidateValue(el, type, optional, false, objectClassName)) { + return NO; + } + } + return YES; + } + if (!value || value == NSNull.null) { + return YES; + } + return NO; + } - switch (property.type) { + switch (type) { case RLMPropertyTypeString: - return [obj isKindOfClass:[NSString class]]; + return [value isKindOfClass:[NSString class]]; case RLMPropertyTypeBool: - if ([obj isKindOfClass:[NSNumber class]]) { - return nsnumber_is_like_bool(obj); + if ([value isKindOfClass:[NSNumber class]]) { + return numberIsBool(value); } return NO; case RLMPropertyTypeDate: - return [obj isKindOfClass:[NSDate class]]; + return [value isKindOfClass:[NSDate class]]; case RLMPropertyTypeInt: - if (NSNumber *number = RLMDynamicCast(obj)) { - return nsnumber_is_like_integer(number); + if (NSNumber *number = RLMDynamicCast(value)) { + return numberIsInteger(number); } return NO; case RLMPropertyTypeFloat: - if (NSNumber *number = RLMDynamicCast(obj)) { - return nsnumber_is_like_float(number); + if (NSNumber *number = RLMDynamicCast(value)) { + return numberIsFloat(number); } return NO; case RLMPropertyTypeDouble: - if (NSNumber *number = RLMDynamicCast(obj)) { - return nsnumber_is_like_double(number); + if (NSNumber *number = RLMDynamicCast(value)) { + return numberIsDouble(number); } return NO; case RLMPropertyTypeData: - return [obj isKindOfClass:[NSData class]]; + return [value isKindOfClass:[NSData class]]; case RLMPropertyTypeAny: return NO; - case RLMPropertyTypeObject: - case RLMPropertyTypeLinkingObjects: { + case RLMPropertyTypeLinkingObjects: + return YES; + case RLMPropertyTypeObject: { // only NSNull, nil, or objects which derive from RLMObject and match the given // object class are valid - RLMObjectBase *objBase = RLMDynamicCast(obj); - return objBase && [objBase->_objectSchema.className isEqualToString:property.objectClassName]; + RLMObjectBase *objBase = RLMDynamicCast(value); + return objBase && [objBase->_objectSchema.className isEqualToString:objectClassName]; } - case RLMPropertyTypeArray: { - if (RLMArray *array = RLMDynamicCast(obj)) { - return [array.objectClassName isEqualToString:property.objectClassName]; - } - if (RLMListBase *list = RLMDynamicCast(obj)) { - return [list._rlmArray.objectClassName isEqualToString:property.objectClassName]; - } - if ([obj conformsToProtocol:@protocol(NSFastEnumeration)]) { - // check each element for compliance - for (id el in (id)obj) { - RLMObjectBase *obj = RLMDynamicCast(el); - if (!obj || ![obj->_objectSchema.className isEqualToString:property.objectClassName]) { - return NO; - } - } - return YES; + } + @throw RLMException(@"Invalid RLMPropertyType specified"); +} + +void RLMThrowTypeError(__unsafe_unretained id const obj, + __unsafe_unretained RLMObjectSchema *const objectSchema, + __unsafe_unretained RLMProperty *const prop) { + @throw RLMException(@"Invalid value '%@' of type '%@' for '%@%s'%s property '%@.%@'.", + obj, [obj class], + prop.objectClassName ?: RLMTypeToString(prop.type), prop.optional ? "?" : "", + prop.array ? " array" : "", objectSchema.className, prop.name); +} + +void RLMValidateValueForProperty(__unsafe_unretained id const obj, + __unsafe_unretained RLMObjectSchema *const objectSchema, + __unsafe_unretained RLMProperty *const prop, + bool validateObjects) { + // This duplicates a lot of the checks in RLMIsObjectValidForProperty() + // for the sake of more specific error messages + if (prop.array) { + // nil is considered equivalent to an empty array for historical reasons + // since we don't support null arrays (only arrays containing null), + // it's not worth the BC break to change this + if (!obj || obj == NSNull.null) { + return; + } + if (![obj conformsToProtocol:@protocol(NSFastEnumeration)]) { + @throw RLMException(@"Invalid value (%@) for '%@%s' array property '%@.%@': value is not enumerable.", + obj, prop.objectClassName ?: RLMTypeToString(prop.type), prop.optional ? "?" : "", + objectSchema.className, prop.name); + } + if (!validateObjects && prop.type == RLMPropertyTypeObject) { + return; + } + + if (RLMArray *array = asRLMArray(obj)) { + if (!checkArrayType(array, prop.type, prop.optional, prop.objectClassName)) { + @throw RLMException(@"RLMArray<%@%s> does not match expected type '%@%s' for property '%@.%@'.", + array.objectClassName ?: RLMTypeToString(array.type), array.optional ? "?" : "", + prop.objectClassName ?: RLMTypeToString(prop.type), prop.optional ? "?" : "", + objectSchema.className, prop.name); } - if (!obj || obj == NSNull.null) { - return YES; + return; + } + + for (id value in obj) { + if (!RLMValidateValue(value, prop.type, prop.optional, false, prop.objectClassName)) { + RLMThrowTypeError(value, objectSchema, prop); } - return NO; } + return; } - @throw RLMException(@"Invalid RLMPropertyType specified"); + + // For create() we want to skip the validation logic for objects because + // we allow much fuzzier matching (any KVC-compatible object with at least + // all the non-defaulted fields), and all the logic for that lives in the + // object store rather than here + if (prop.type == RLMPropertyTypeObject && !validateObjects) { + return; + } + if (RLMIsObjectValidForProperty(obj, prop)) { + return; + } + + RLMThrowTypeError(obj, objectSchema, prop); +} + +BOOL RLMIsObjectValidForProperty(__unsafe_unretained id const obj, + __unsafe_unretained RLMProperty *const property) { + return RLMValidateValue(obj, property.type, property.optional, property.array, property.objectClassName); } NSDictionary *RLMDefaultValuesForObjectSchema(__unsafe_unretained RLMObjectSchema *const objectSchema) { @@ -211,7 +284,7 @@ BOOL RLMIsObjectValidForProperty(__unsafe_unretained id const obj, } NSException *RLMException(std::exception const& exception) { - return RLMException(@"%@", @(exception.what())); + return RLMException(@"%s", exception.what()); } NSError *RLMMakeError(RLMError code, std::exception const& exception) { @@ -251,12 +324,6 @@ BOOL RLMIsObjectValidForProperty(__unsafe_unretained id const obj, @"Category": category}]; } -NSError *RLMMakeError(NSException *exception) { - return [NSError errorWithDomain:RLMErrorDomain - code:0 - userInfo:@{NSLocalizedDescriptionKey: exception.reason}]; -} - void RLMSetErrorOrThrow(NSError *error, NSError **outError) { if (outError) { *outError = error; @@ -270,42 +337,6 @@ void RLMSetErrorOrThrow(NSError *error, NSError **outError) { } } -// Determines if class1 descends from class2 -static inline BOOL RLMIsSubclass(Class class1, Class class2) { - class1 = class_getSuperclass(class1); - return RLMIsKindOfClass(class1, class2); -} - -static bool treatFakeObjectAsRLMObject = false; - -void RLMSetTreatFakeObjectAsRLMObject(BOOL flag) { - treatFakeObjectAsRLMObject = flag; -} - -BOOL RLMIsObjectOrSubclass(Class klass) { - if (RLMIsKindOfClass(klass, RLMObjectBase.class)) { - return YES; - } - - if (treatFakeObjectAsRLMObject) { - static Class FakeObjectClass = NSClassFromString(@"FakeObject"); - return RLMIsKindOfClass(klass, FakeObjectClass); - } - return NO; -} - -BOOL RLMIsObjectSubclass(Class klass) { - if (RLMIsSubclass(class_getSuperclass(klass), RLMObjectBase.class)) { - return YES; - } - - if (treatFakeObjectAsRLMObject) { - static Class FakeObjectClass = NSClassFromString(@"FakeObject"); - return RLMIsSubclass(klass, FakeObjectClass); - } - return NO; -} - BOOL RLMIsDebuggerAttached() { int name[] = { diff --git a/Pods/Realm/Realm/Realm.modulemap b/Pods/Realm/Realm/Realm.modulemap index ef2d102..42845f6 100644 --- a/Pods/Realm/Realm/Realm.modulemap +++ b/Pods/Realm/Realm/Realm.modulemap @@ -7,15 +7,17 @@ framework module Realm { explicit module Private { header "RLMAccessor.h" header "RLMArray_Private.h" + header "RLMCollection_Private.h" header "RLMListBase.h" + header "RLMObject_Private.h" header "RLMObjectBase_Dynamic.h" + header "RLMObjectBase_Private.h" header "RLMObjectSchema_Private.h" header "RLMObjectStore.h" - header "RLMObject_Private.h" header "RLMOptionalBase.h" header "RLMProperty_Private.h" - header "RLMRealmConfiguration_Private.h" header "RLMRealm_Private.h" + header "RLMRealmConfiguration_Private.h" header "RLMResults_Private.h" header "RLMSchema_Private.h" header "RLMSyncConfiguration_Private.h" diff --git a/Pods/Realm/build.sh b/Pods/Realm/build.sh index 00a9d8f..d38787b 100755 --- a/Pods/Realm/build.sh +++ b/Pods/Realm/build.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash ################################################################################## # Custom build tool for Realm Objective-C binding. @@ -68,6 +68,7 @@ command: test-osx: tests OS X framework test-osx-swift: tests RealmSwift OS X framework verify: verifies docs, osx, osx-swift, ios-static, ios-dynamic, ios-swift, ios-device in both Debug and Release configurations, swiftlint + verify-osx-object-server: downloads the Realm Object Server and runs the Objective-C and Swift integration tests docs: builds docs in docs/output examples: builds all examples examples-ios: builds all static iOS examples @@ -94,6 +95,10 @@ EOF # Xcode Helpers ###################################### +xcode_version_major() { + echo "${REALM_XCODE_VERSION%%.*}" +} + xcode() { mkdir -p build/DerivedData CMD="xcodebuild -IDECustomDerivedDataLocation=build/DerivedData $@" @@ -118,6 +123,11 @@ xc() { fi } +xctest() { + xc "$@" build + xc "$@" test +} + copy_bcsymbolmap() { find "$1" -name '*.bcsymbolmap' -type f -exec cp {} "$2" \; } @@ -141,7 +151,11 @@ build_combined() { destination="Apple Watch - 42mm" elif [[ "$os" == "appletvos" ]]; then os_name="tvos" - destination="Apple TV 1080p" + if (( $(xcode_version_major) >= 9 )); then + destination="Apple TV" + else + destination="Apple TV 1080p" + fi fi # Derive build paths @@ -191,7 +205,9 @@ move_to_clean_dir() { test_ios_static() { destination="$1" xc "-scheme 'Realm iOS static' -configuration $CONFIGURATION -sdk iphonesimulator -destination '$destination' build" - xc "-scheme 'Realm iOS static' -configuration $CONFIGURATION -sdk iphonesimulator -destination '$destination' test 'ARCHS=\$(ARCHS_STANDARD_32_BIT)'" + if (( $(xcode_version_major) < 9 )); then + xc "-scheme 'Realm iOS static' -configuration $CONFIGURATION -sdk iphonesimulator -destination '$destination' test 'ARCHS=\$(ARCHS_STANDARD_32_BIT)'" + fi # Xcode's depending tracking is lacking and it doesn't realize that the Realm static framework's static library # needs to be recreated when the active architectures change. Help Xcode out by removing the static library. @@ -207,14 +223,23 @@ test_ios_static() { ###################################### test_devices() { - serial_numbers_str=$(system_profiler SPUSBDataType | grep "Serial Number: ") - serial_numbers=() - while read -r line; do - number=${line:15} # Serial number starts at position 15 - if [[ ${#number} == 40 ]]; then + local serial_numbers=() + local awk_script=" + /^ +Vendor ID: / { is_apple = 0; } + /^ +Vendor ID: 0x05[aA][cC] / { is_apple = 1; } + /^ +Serial Number: / { + if (is_apple) { + match(\$0, /^ +Serial Number: /); + print substr(\$0, RLENGTH + 1); + } + } + " + local serial_numbers_text=$(/usr/sbin/system_profiler SPUSBDataType | /usr/bin/awk "$awk_script") + while read -r number; do + if [[ "$number" != "" ]]; then serial_numbers+=("$number") fi - done <<< "$serial_numbers_str" + done <<< "$serial_numbers_text" if [[ ${#serial_numbers[@]} == 0 ]]; then echo "At least one iOS/tvOS device must be connected to this computer to run device tests" if [ -z "${JENKINS_HOME}" ]; then @@ -283,83 +308,61 @@ fi # Downloading ###################################### -kill_object_server() { - (pgrep -f realm-object-server || true) | while read pid; do - kill $pid 2>/dev/null - done -} +download_common() { + local download_type=$1 tries_left=3 version url error temp_dir temp_path tar_path + + if [ "$download_type" == "core" ]; then + version=$REALM_CORE_VERSION + url="https://static.realm.io/downloads/core/realm-core-${version}.tar.xz" + elif [ "$download_type" == "sync" ]; then + version=$REALM_SYNC_VERSION + url="https://static.realm.io/downloads/sync/realm-sync-cocoa-${version}.tar.xz" + else + echo "Unknown dowload_type: $download_type" + exit 1 + fi -download_object_server() { - local archive_name="realm-object-server-bundled_node_darwin-developer-$REALM_OBJECT_SERVER_VERSION.tar.gz" - curl -L -O "https://static.realm.io/downloads/object-server/$archive_name" - rm -rf sync - mkdir sync - tar xf $archive_name -C sync - rm $archive_name - cp Configuration/object-server-config.yml sync/object-server/configuration.yml - touch "sync/object-server/do_not_open_browser" -} + echo "Downloading dependency: ${download_type} ${version}" -download_core() { - echo "Downloading dependency: core ${REALM_CORE_VERSION}" - TMP_DIR="$TMPDIR/core_bin" - mkdir -p "${TMP_DIR}" - CORE_TMP_TAR="${TMP_DIR}/core-${REALM_CORE_VERSION}.tar.xz.tmp" - CORE_TAR="${TMP_DIR}/core-${REALM_CORE_VERSION}.tar.xz" - if [ ! -f "${CORE_TAR}" ]; then - local CORE_URL="https://static.realm.io/downloads/core/realm-core-${REALM_CORE_VERSION}.tar.xz" - set +e # temporarily disable immediate exit - local ERROR # sweeps the exit code unless declared separately - ERROR=$(curl --fail --silent --show-error --location "$CORE_URL" --output "${CORE_TMP_TAR}" 2>&1 >/dev/null) - if [[ $? -ne 0 ]]; then - echo "Downloading core failed:\n${ERROR}" - exit 1 + if [ -z "$TMPDIR" ]; then + TMPDIR='/tmp' + fi + temp_dir=$(dirname "$TMPDIR/waste")/${download_type}_bin + mkdir -p "$temp_dir" + tar_path="${temp_dir}/${download_type}-${version}.tar.xz" + temp_path="${tar_path}.tmp" + + while [ 0 -lt $tries_left ] && [ ! -f "$tar_path" ]; do + if ! error=$(/usr/bin/curl --fail --silent --show-error --location "$url" --output "$temp_path" 2>&1); then + tries_left=$[$tries_left-1] + else + mv "$temp_path" "$tar_path" fi - set -e # re-enable flag - mv "${CORE_TMP_TAR}" "${CORE_TAR}" + done + + if [ ! -f "$tar_path" ]; then + printf "Downloading ${download_type} failed:\n\t$url\n\t$error\n" + exit 1 fi ( - cd "${TMP_DIR}" - rm -rf core - tar xf "${CORE_TAR}" --xz - mv core core-${REALM_CORE_VERSION} + cd "$temp_dir" + rm -rf "$download_type" + tar xf "$tar_path" --xz + mv core "${download_type}-${version}" ) - rm -rf core-${REALM_CORE_VERSION} core - mv ${TMP_DIR}/core-${REALM_CORE_VERSION} . - ln -s core-${REALM_CORE_VERSION} core + rm -rf "${download_type}-${version}" core + mv "${temp_dir}/${download_type}-${version}" . + ln -s "${download_type}-${version}" core } -download_sync() { - echo "Downloading dependency: sync ${REALM_SYNC_VERSION}" - TMP_DIR="$TMPDIR/sync_bin" - mkdir -p "${TMP_DIR}" - SYNC_TMP_TAR="${TMP_DIR}/sync-${REALM_SYNC_VERSION}.tar.xz.tmp" - SYNC_TAR="${TMP_DIR}/sync-${REALM_SYNC_VERSION}.tar.xz" - if [ ! -f "${SYNC_TAR}" ]; then - local SYNC_URL="https://static.realm.io/downloads/sync/realm-sync-cocoa-${REALM_SYNC_VERSION}.tar.xz" - set +e # temporarily disable immediate exit - local ERROR # sweeps the exit code unless declared separately - ERROR=$(curl --fail --silent --show-error --location "$SYNC_URL" --output "${SYNC_TMP_TAR}" 2>&1 >/dev/null) - if [[ $? -ne 0 ]]; then - echo "Downloading sync failed:\n${ERROR}" - exit 1 - fi - set -e # re-enable flag - mv "${SYNC_TMP_TAR}" "${SYNC_TAR}" - fi - - ( - cd "${TMP_DIR}" - rm -rf sync - tar xf "${SYNC_TAR}" --xz - mv core sync-${REALM_SYNC_VERSION} - ) +download_core() { + download_common "core" +} - rm -rf sync-${REALM_SYNC_VERSION} core - mv ${TMP_DIR}/sync-${REALM_SYNC_VERSION} . - ln -s sync-${REALM_SYNC_VERSION} core +download_sync() { + download_common "sync" } ###################################### @@ -369,14 +372,18 @@ download_sync() { COMMAND="$1" # Use Debug config if command ends with -debug, otherwise default to Release +# Set IS_RUNNING_PACKAGING when running packaging steps to avoid running iOS static tests with Xcode 8.3.3 case "$COMMAND" in *-debug) COMMAND="${COMMAND%-debug}" CONFIGURATION="Debug" ;; - *) CONFIGURATION=${CONFIGURATION:-Release} + package-*) + IS_RUNNING_PACKAGING=1 + ;; esac -export CONFIGURATION +export CONFIGURATION=${CONFIGURATION:-Release} +export IS_RUNNING_PACKAGING=${IS_RUNNING_PACKAGING:-0} # Pre-choose Xcode and Swift versions for those operations that do not set them REALM_XCODE_VERSION=${xcode_version:-$REALM_XCODE_VERSION} @@ -394,46 +401,7 @@ case "$COMMAND" in # Clean ###################################### "clean") - find . -type d -name build -exec rm -r "{}" +\; - exit 0 - ;; - - ###################################### - # Object Server - ###################################### - "download-object-server") - download_object_server - exit 0 - ;; - - "start-object-server") - kill_object_server - ./sync/start-object-server.command - exit 0 - ;; - - "reset-object-server-between-tests") - # Leave the server files alone to avoid 'bad_server_ident' errors - rm -rf "~/Library/Application Support/xctest" - rm -rf "~/Library/Application Support/io.realm.TestHost" - rm -rf "~/Library/Application Support/xctest-child" - exit 0 - ;; - - "reset-object-server") - kill_object_server - # Add a short delay, so file system doesn't complain about files in use - sleep 1 - package="${source_root}/sync" - for file in "$package"/realm-object-server-*; do - if [ -d "$file" ]; then - package="$file" - break - fi - done - rm -rf "$package/object-server/root_dir/" - rm -rf "$package/object-server/temp_dir/" - sh build.sh reset-object-server-between-tests + find . -type d -name build -exec rm -r "{}" + exit 0 ;; @@ -620,21 +588,20 @@ case "$COMMAND" in exit 0 ;; - "test-ios7-static") - test_ios_static "name=iPhone 5S,OS=7.1" - exit 0 - ;; - "test-ios-dynamic") xc "-scheme Realm -configuration $CONFIGURATION -sdk iphonesimulator -destination 'name=iPhone 6' build" - xc "-scheme Realm -configuration $CONFIGURATION -sdk iphonesimulator -destination 'name=iPhone 6' test 'ARCHS=\$(ARCHS_STANDARD_32_BIT)'" + if (( $(xcode_version_major) < 9 )); then + xc "-scheme Realm -configuration $CONFIGURATION -sdk iphonesimulator -destination 'name=iPhone 6' test 'ARCHS=\$(ARCHS_STANDARD_32_BIT)'" + fi xc "-scheme Realm -configuration $CONFIGURATION -sdk iphonesimulator -destination 'name=iPhone 6' test" exit 0 ;; "test-ios-swift") xc "-scheme RealmSwift -configuration $CONFIGURATION -sdk iphonesimulator -destination 'name=iPhone 6' build" - xc "-scheme RealmSwift -configuration $CONFIGURATION -sdk iphonesimulator -destination 'name=iPhone 6' test 'ARCHS=\$(ARCHS_STANDARD_32_BIT)'" + if (( $(xcode_version_major) < 9 )); then + xc "-scheme RealmSwift -configuration $CONFIGURATION -sdk iphonesimulator -destination 'name=iPhone 6' test 'ARCHS=\$(ARCHS_STANDARD_32_BIT)'" + fi xc "-scheme RealmSwift -configuration $CONFIGURATION -sdk iphonesimulator -destination 'name=iPhone 6' test" exit 0 ;; @@ -658,12 +625,22 @@ case "$COMMAND" in ;; "test-tvos") - xc "-scheme Realm -configuration $CONFIGURATION -sdk appletvsimulator -destination 'name=Apple TV 1080p' test" + if (( $(xcode_version_major) >= 9 )); then + destination="Apple TV" + else + destination="Apple TV 1080p" + fi + xctest "-scheme Realm -configuration $CONFIGURATION -sdk appletvsimulator -destination 'name=$destination'" exit $? ;; "test-tvos-swift") - xc "-scheme RealmSwift -configuration $CONFIGURATION -sdk appletvsimulator -destination 'name=Apple TV 1080p' test" + if (( $(xcode_version_major) >= 9 )); then + destination="Apple TV" + else + destination="Apple TV 1080p" + fi + xctest "-scheme RealmSwift -configuration $CONFIGURATION -sdk appletvsimulator -destination 'name=$destination'" exit $? ;; @@ -676,17 +653,17 @@ case "$COMMAND" in if [[ "$CONFIGURATION" == "Debug" ]]; then COVERAGE_PARAMS="GCC_GENERATE_TEST_COVERAGE_FILES=YES GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES" fi - xc "-scheme Realm -configuration $CONFIGURATION test $COVERAGE_PARAMS" + xctest "-scheme Realm -configuration $CONFIGURATION $COVERAGE_PARAMS" exit 0 ;; "test-osx-swift") - xc "-scheme RealmSwift -configuration $CONFIGURATION test" + xctest "-scheme RealmSwift -configuration $CONFIGURATION" exit 0 ;; "test-osx-object-server") - xc "-scheme 'Object Server Tests' -configuration $CONFIGURATION -sdk macosx test" + xctest "-scheme 'Object Server Tests' -configuration $CONFIGURATION -sdk macosx" exit 0 ;; @@ -702,8 +679,6 @@ case "$COMMAND" in sh build.sh verify-osx-swift-debug sh build.sh verify-ios-static sh build.sh verify-ios-static-debug - sh build.sh verify-ios7-static - sh build.sh verify-ios7-static-debug sh build.sh verify-ios-dynamic sh build.sh verify-ios-dynamic-debug sh build.sh verify-ios-swift @@ -770,10 +745,6 @@ case "$COMMAND" in sh build.sh examples-ios ;; - "verify-ios7-static") - sh build.sh test-ios7-static - ;; - "verify-ios-dynamic") sh build.sh test-ios-dynamic ;; @@ -830,9 +801,7 @@ case "$COMMAND" in ;; "verify-osx-object-server") - sh build.sh download-object-server sh build.sh test-osx-object-server - sh build.sh reset-object-server exit 0 ;; @@ -880,7 +849,11 @@ case "$COMMAND" in "examples-ios-swift") sh build.sh prelaunch-simulator - workspace="examples/ios/swift-$REALM_SWIFT_VERSION/RealmExamples.xcworkspace" + workspace="examples/ios/swift/RealmExamples.xcworkspace" + if [[ ! -d "$workspace" ]]; then + workspace="${workspace/swift/swift-$REALM_SWIFT_VERSION}" + fi + xc "-workspace $workspace -scheme Simple -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" xc "-workspace $workspace -scheme TableView -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" xc "-workspace $workspace -scheme Migration -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" @@ -896,15 +869,31 @@ case "$COMMAND" in "examples-tvos") workspace="examples/tvos/objc/RealmExamples.xcworkspace" - xc "-workspace $workspace -scheme DownloadCache -configuration $CONFIGURATION -destination 'name=Apple TV 1080p' build ${CODESIGN_PARAMS}" - xc "-workspace $workspace -scheme PreloadedData -configuration $CONFIGURATION -destination 'name=Apple TV 1080p' build ${CODESIGN_PARAMS}" + if (( $(xcode_version_major) >= 9 )); then + destination="Apple TV" + else + destination="Apple TV 1080p" + fi + + xc "-workspace $workspace -scheme DownloadCache -configuration $CONFIGURATION -destination 'name=$destination' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme PreloadedData -configuration $CONFIGURATION -destination 'name=$destination' build ${CODESIGN_PARAMS}" exit 0 ;; "examples-tvos-swift") - workspace="examples/tvos/swift-$REALM_SWIFT_VERSION/RealmExamples.xcworkspace" - xc "-workspace $workspace -scheme DownloadCache -configuration $CONFIGURATION -destination 'name=Apple TV 1080p' build ${CODESIGN_PARAMS}" - xc "-workspace $workspace -scheme PreloadedData -configuration $CONFIGURATION -destination 'name=Apple TV 1080p' build ${CODESIGN_PARAMS}" + workspace="examples/tvos/swift/RealmExamples.xcworkspace" + if [[ ! -d "$workspace" ]]; then + workspace="${workspace/swift/swift-$REALM_SWIFT_VERSION}" + fi + + if (( $(xcode_version_major) >= 9 )); then + destination="Apple TV" + else + destination="Apple TV 1080p" + fi + + xc "-workspace $workspace -scheme DownloadCache -configuration $CONFIGURATION -destination 'name=$destination' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme PreloadedData -configuration $CONFIGURATION -destination 'name=$destination' build ${CODESIGN_PARAMS}" exit 0 ;; @@ -913,7 +902,7 @@ case "$COMMAND" in ###################################### "get-version") version_file="Realm/Realm-Info.plist" - echo "$(PlistBuddy -c "Print :CFBundleVersion" "$version_file")" + echo "$(PlistBuddy -c "Print :CFBundleShortVersionString" "$version_file")" exit 0 ;; @@ -925,8 +914,11 @@ case "$COMMAND" in echo "You must specify a version." exit 1 fi + # The bundle version can contain only three groups of digits separated by periods, + # so strip off any -beta.x tag from the end of the version string. + bundle_version=$(echo "$realm_version" | cut -d - -f 1) for version_file in $version_files; do - PlistBuddy -c "Set :CFBundleVersion $realm_version" "$version_file" + PlistBuddy -c "Set :CFBundleVersion $bundle_version" "$version_file" PlistBuddy -c "Set :CFBundleShortVersionString $realm_version" "$version_file" done sed -i '' "s/^VERSION=.*/VERSION=$realm_version/" dependencies.list @@ -938,10 +930,12 @@ case "$COMMAND" in ###################################### "binary-has-bitcode") + # Disable pipefail as grep -q will make otool fail due to exiting + # before reading all the output + set +o pipefail + BINARY="$2" - # Although grep has a '-q' flag to prevent logging to stdout, grep - # behaves differently when used, so redirect stdout to /dev/null. - if otool -l "$BINARY" | grep "segname __LLVM" > /dev/null 2>&1; then + if otool -l "$BINARY" | grep -q "segname __LLVM"; then exit 0 fi # Work around rdar://21826157 by checking for bitcode in thin binaries @@ -950,7 +944,8 @@ case "$COMMAND" in archs="$(lipo -info "$BINARY" | rev | cut -d ':' -f1 | rev)" archs_array=( $archs ) - if [[ ${#archs_array[@]} < 2 ]]; then + if [[ ${#archs_array[@]} -lt 2 ]]; then + echo 'Error: Built library is not a fat binary' exit 1 # Early exit if not a fat binary fi @@ -962,6 +957,7 @@ case "$COMMAND" in exit 0 fi done + echo 'Error: Built library does not contain bitcode' exit 1 ;; @@ -973,6 +969,10 @@ case "$COMMAND" in sh build.sh download-sync rm core mv sync-* core + mv core/librealm-ios.a core/librealmcore-ios.a + mv core/librealm-macosx.a core/librealmcore-macosx.a + mv core/librealm-tvos.a core/librealmcore-tvos.a + mv core/librealm-watchos.a core/librealmcore-watchos.a fi if [[ "$2" != "swift" ]]; then @@ -994,17 +994,18 @@ EOM mkdir -p include mv core/include include/core - mkdir -p include/impl/apple include/util/apple include/sync/impl + mkdir -p include/impl/apple include/util/apple include/sync/impl/apple cp Realm/*.hpp include cp Realm/ObjectStore/src/*.hpp include cp Realm/ObjectStore/src/sync/*.hpp include/sync cp Realm/ObjectStore/src/sync/impl/*.hpp include/sync/impl + cp Realm/ObjectStore/src/sync/impl/apple/*.hpp include/sync/impl/apple cp Realm/ObjectStore/src/impl/*.hpp include/impl cp Realm/ObjectStore/src/impl/apple/*.hpp include/impl/apple cp Realm/ObjectStore/src/util/*.hpp include/util cp Realm/ObjectStore/src/util/apple/*.hpp include/util/apple - touch Realm/RLMPlatform.h + echo '' > Realm/RLMPlatform.h if [ -n "$COCOAPODS_VERSION" ]; then # This variable is set for the prepare_command available # from the 1.0 prereleases, which requires a different @@ -1028,9 +1029,16 @@ EOM "ci-pr") mkdir -p build/reports + export REALM_DISABLE_ANALYTICS=1 + export REALM_DISABLE_UPDATE_CHECKER=1 # FIXME: Re-enable once CI can properly unlock the keychain export REALM_DISABLE_METADATA_ENCRYPTION=1 + # strip off the ios|tvos version specifier, e.g. the last part of: `ios-device-objc-ios8` + if [[ "$target" =~ ^((ios|tvos)-device(-(objc|swift))?)(-(ios|tvos)[[:digit:]]+)?$ ]]; then + export target=${BASH_REMATCH[1]} + fi + if [ "$target" = "docs" ]; then sh build.sh set-swift-version sh build.sh verify-docs @@ -1041,14 +1049,26 @@ EOM export CONFIGURATION=$configuration export REALM_EXTRA_BUILD_ARGUMENTS='GCC_GENERATE_DEBUGGING_SYMBOLS=NO REALM_PREFIX_HEADER=Realm/RLMPrefix.h' sh build.sh prelaunch-simulator - rm ~/Library/Logs/CoreSimulator/CoreSimulator.log - # Verify that no Realm files still exist - ! find ~/Library/Developer/CoreSimulator/Devices/ -name '*.realm' | grep -q . + + source $(brew --prefix nvm)/nvm.sh + export REALM_NODE_PATH="$(nvm which 8)" + + # Reset CoreSimulator.log + mkdir -p ~/Library/Logs/CoreSimulator + echo > ~/Library/Logs/CoreSimulator/CoreSimulator.log + + if [ -d ~/Library/Developer/CoreSimulator/Devices/ ]; then + # Verify that no Realm files still exist + ! find ~/Library/Developer/CoreSimulator/Devices/ -name '*.realm' | grep -q . + fi failed=0 sh build.sh verify-$target 2>&1 | tee build/build.log | xcpretty -r junit -o build/reports/junit.xml || failed=1 if [ "$failed" = "1" ] && cat build/build.log | grep -E 'DTXProxyChannel|DTXChannel|out of date and needs to be rebuilt|operation never finished bootstrapping'; then echo "Known Xcode error detected. Running job again." + if cat build/build.log | grep -E 'out of date and needs to be rebuilt'; then + rm -rf build/DerivedData + fi failed=0 sh build.sh verify-$target | tee build/build.log | xcpretty -r junit -o build/reports/junit.xml || failed=1 elif [ "$failed" = "1" ] && tail ~/Library/Logs/CoreSimulator/CoreSimulator.log | grep -E "Operation not supported|Failed to lookup com.apple.coreservices.lsuseractivity.simulatorsupport"; then @@ -1057,8 +1077,8 @@ EOM sh build.sh verify-$target | tee build/build.log | xcpretty -r junit -o build/reports/junit.xml || failed=1 fi if [ "$failed" = "1" ]; then - echo "\n\n***\nbuild/build.log\n***\n\n" && cat build/build.log - echo "\n\n***\nCoreSimulator.log\n***\n\n" && tail -n2000 ~/Library/Logs/CoreSimulator/CoreSimulator.log + echo "\n\n***\nbuild/build.log\n***\n\n" && cat build/build.log || true + echo "\n\n***\nCoreSimulator.log\n***\n\n" && cat ~/Library/Logs/CoreSimulator/CoreSimulator.log exit 1 fi fi @@ -1075,14 +1095,13 @@ EOM ###################################### "package-examples") - cd tightdb_objc ./scripts/package_examples.rb zip --symlinks -r realm-examples.zip examples -x "examples/installation/*" ;; "package-test-examples") - if ! VERSION=$(echo realm-objc-*.zip | grep -o '\d*\.\d*\.\d*-[a-z]*'); then - VERSION=$(echo realm-objc-*.zip | grep -o '\d*\.\d*\.\d*') + if ! VERSION=$(echo realm-objc-*.zip | egrep -o '\d*\.\d*\.\d*-[a-z]*(\.\d*)?'); then + VERSION=$(echo realm-objc-*.zip | egrep -o '\d*\.\d*\.\d*') fi OBJC="realm-objc-${VERSION}" SWIFT="realm-swift-${VERSION}" @@ -1109,112 +1128,129 @@ EOM ;; "package-ios-static") - cd tightdb_objc - sh build.sh prelaunch-simulator - sh build.sh test-ios-static sh build.sh ios-static cd build/ios-static - zip --symlinks -r realm-framework-ios.zip Realm.framework + zip --symlinks -r realm-framework-ios-static.zip Realm.framework ;; - "package-ios-dynamic") - cd tightdb_objc - + "package-ios") sh build.sh prelaunch-simulator sh build.sh ios-dynamic cd build/ios - zip --symlinks -r realm-dynamic-framework-ios.zip Realm.framework + zip --symlinks -r realm-framework-ios.zip Realm.framework ;; "package-osx") - cd tightdb_objc - sh build.sh test-osx + sh build.sh osx cd build/DerivedData/Realm/Build/Products/Release zip --symlinks -r realm-framework-osx.zip Realm.framework ;; - "package-ios-swift") - cd tightdb_objc - for version in 8.0 8.1 8.2; do - REALM_XCODE_VERSION=$version - REALM_SWIFT_VERSION= - set_xcode_and_swift_versions - sh build.sh prelaunch-simulator - sh build.sh ios-swift - done + "package-watchos") + sh build.sh prelaunch-simulator + sh build.sh watchos - cd build/ios - zip --symlinks -r realm-swift-framework-ios.zip swift-3.0 swift-3.0.1 swift-3.0.2 + cd build/watchos + zip --symlinks -r realm-framework-watchos.zip Realm.framework + ;; + + "package-tvos") + sh build.sh prelaunch-simulator + sh build.sh tvos + + cd build/tvos + zip --symlinks -r realm-framework-tvos.zip Realm.framework ;; - "package-osx-swift") - cd tightdb_objc - for version in 8.0 8.1 8.2; do + package-*-swift) + PLATFORM=$(echo $COMMAND | cut -d - -f 2) + for version in 8.3.3 9.0 9.1 9.2 9.3; do REALM_XCODE_VERSION=$version REALM_SWIFT_VERSION= set_xcode_and_swift_versions sh build.sh prelaunch-simulator - sh build.sh osx-swift + sh build.sh $PLATFORM-swift done - cd build/osx - zip --symlinks -r realm-swift-framework-osx.zip swift-3.0 swift-3.0.1 swift-3.0.2 + cd build/$PLATFORM + ln -s swift-4.0 swift-3.2 + ln -s swift-4.0.2 swift-3.2.2 + ln -s swift-4.0.2 swift-3.2.3 + ln -s swift-4.0.2 swift-4.0.3 + ln -s swift-4.1 swift-3.3 + ln -s swift-4.1 swift-4.1.2 + zip --symlinks -r realm-swift-framework-$PLATFORM.zip swift-3.1 swift-3.2 swift-3.2.2 swift-3.2.3 swift-3.3 swift-4.0 swift-4.0.2 swift-4.0.3 swift-4.1 swift-4.1.2 ;; - "package-watchos") - cd tightdb_objc - sh build.sh watchos + package-*-swift-3.2) + PLATFORM=$(echo $COMMAND | cut -d - -f 2) + mkdir -p build/$PLATFORM + cd build/$PLATFORM + ln -s swift-4.0 swift-3.2 + zip --symlinks -r realm-swift-framework-$PLATFORM-swift-3.2.zip swift-3.2 + ;; - cd build/watchos - zip --symlinks -r realm-framework-watchos.zip Realm.framework + package-*-swift-3.2.2) + PLATFORM=$(echo $COMMAND | cut -d - -f 2) + mkdir -p build/$PLATFORM + cd build/$PLATFORM + ln -s swift-4.0.2 swift-3.2.2 + zip --symlinks -r realm-swift-framework-$PLATFORM-swift-3.2.2.zip swift-3.2.2 ;; - "package-watchos-swift") - cd tightdb_objc - for version in 8.0 8.1 8.2; do - REALM_XCODE_VERSION=$version - REALM_SWIFT_VERSION= - set_xcode_and_swift_versions - sh build.sh prelaunch-simulator - sh build.sh watchos-swift - done + package-*-swift-3.2.3) + PLATFORM=$(echo $COMMAND | cut -d - -f 2) + mkdir -p build/$PLATFORM + cd build/$PLATFORM + ln -s swift-4.0.2 swift-3.2.3 + zip --symlinks -r realm-swift-framework-$PLATFORM-swift-3.2.3.zip swift-3.2.3 + ;; - cd build/watchos - zip --symlinks -r realm-swift-framework-watchos.zip swift-3.0 swift-3.0.1 swift-3.0.2 + package-*-swift-3.3) + PLATFORM=$(echo $COMMAND | cut -d - -f 2) + mkdir -p build/$PLATFORM + cd build/$PLATFORM + ln -s swift-4.1 swift-3.3 + zip --symlinks -r realm-swift-framework-$PLATFORM-swift-3.3.zip swift-3.3 ;; - "package-tvos") - cd tightdb_objc - sh build.sh tvos + package-*-swift-4.0.3) + PLATFORM=$(echo $COMMAND | cut -d - -f 2) + mkdir -p build/$PLATFORM + cd build/$PLATFORM + ln -s swift-4.0.2 swift-4.0.3 + zip --symlinks -r realm-swift-framework-$PLATFORM-swift-4.0.3.zip swift-4.0.3 + ;; - cd build/tvos - zip --symlinks -r realm-framework-tvos.zip Realm.framework + package-*-swift-4.1.2) + PLATFORM=$(echo $COMMAND | cut -d - -f 2) + mkdir -p build/$PLATFORM + cd build/$PLATFORM + ln -s swift-4.1 swift-4.1.2 + zip --symlinks -r realm-swift-framework-$PLATFORM-swift-4.1.2.zip swift-4.1.2 ;; - "package-tvos-swift") - cd tightdb_objc - for version in 8.0 8.1 8.2; do - REALM_XCODE_VERSION=$version - REALM_SWIFT_VERSION= - set_xcode_and_swift_versions - sh build.sh prelaunch-simulator - sh build.sh tvos-swift - done + package-*-swift-*) + PLATFORM=$(echo $COMMAND | cut -d - -f 2) + REALM_SWIFT_VERSION=$(echo $COMMAND | cut -d - -f 4) + REALM_XCODE_VERSION= - cd build/tvos - zip --symlinks -r realm-swift-framework-tvos.zip swift-3.0 swift-3.0.1 swift-3.0.2 + set_xcode_and_swift_versions + sh build.sh prelaunch-simulator + sh build.sh $PLATFORM-swift + + cd build/$PLATFORM + zip --symlinks -r realm-swift-framework-$PLATFORM-swift-$REALM_SWIFT_VERSION.zip swift-$REALM_SWIFT_VERSION ;; "package-release") LANG="$2" TEMPDIR=$(mktemp -d $TMPDIR/realm-release-package-${LANG}.XXXX) - cd tightdb_objc VERSION=$(sh build.sh get-version) - cd .. FOLDER=${TEMPDIR}/realm-${LANG}-${VERSION} @@ -1232,12 +1268,12 @@ EOM ( cd ${FOLDER}/ios/static - unzip ${WORKSPACE}/realm-framework-ios.zip + unzip ${WORKSPACE}/realm-framework-ios-static.zip ) ( cd ${FOLDER}/ios/dynamic - unzip ${WORKSPACE}/realm-dynamic-framework-ios.zip + unzip ${WORKSPACE}/realm-framework-ios.zip ) ( @@ -1252,27 +1288,35 @@ EOM else ( cd ${FOLDER}/osx - unzip ${WORKSPACE}/realm-swift-framework-osx.zip + for f in ${WORKSPACE}/realm-swift-framework-osx-swift-*.zip; do + unzip "$f" + done ) ( cd ${FOLDER}/ios - unzip ${WORKSPACE}/realm-swift-framework-ios.zip + for f in ${WORKSPACE}/realm-swift-framework-ios-swift-*.zip; do + unzip "$f" + done ) ( cd ${FOLDER}/watchos - unzip ${WORKSPACE}/realm-swift-framework-watchos.zip + for f in ${WORKSPACE}/realm-swift-framework-watchos-swift-*.zip; do + unzip "$f" + done ) ( cd ${FOLDER}/tvos - unzip ${WORKSPACE}/realm-swift-framework-tvos.zip + for f in ${WORKSPACE}/realm-swift-framework-tvos-swift-*.zip; do + unzip "$f" + done ) fi ( - cd ${WORKSPACE}/tightdb_objc + cd ${WORKSPACE} cp -R plugin ${FOLDER} cp LICENSE ${FOLDER}/LICENSE.txt if [[ "${LANG}" == "objc" ]]; then @@ -1326,44 +1370,45 @@ EOF WORKSPACE="$(cd "$WORKSPACE" && pwd)" export WORKSPACE cd $WORKSPACE - git clone --recursive $REALM_SOURCE tightdb_objc + git clone --recursive $REALM_SOURCE realm-cocoa + cd realm-cocoa echo 'Packaging iOS' - sh tightdb_objc/build.sh package-ios-static - cp tightdb_objc/build/ios-static/realm-framework-ios.zip . - sh tightdb_objc/build.sh package-ios-dynamic - cp tightdb_objc/build/ios/realm-dynamic-framework-ios.zip . - sh tightdb_objc/build.sh package-ios-swift - cp tightdb_objc/build/ios/realm-swift-framework-ios.zip . + sh build.sh package-ios-static + cp build/ios-static/realm-framework-ios-static.zip .. + sh build.sh package-ios + cp build/ios/realm-framework-ios.zip .. + sh build.sh package-ios-swift + cp build/ios/realm-swift-framework-ios.zip .. echo 'Packaging OS X' - sh tightdb_objc/build.sh package-osx - cp tightdb_objc/build/DerivedData/Realm/Build/Products/Release/realm-framework-osx.zip . - sh tightdb_objc/build.sh package-osx-swift - cp tightdb_objc/build/osx/realm-swift-framework-osx.zip . + sh build.sh package-osx + cp build/DerivedData/Realm/Build/Products/Release/realm-framework-osx.zip .. + sh build.sh package-osx-swift + cp build/osx/realm-swift-framework-osx.zip .. echo 'Packaging watchOS' - sh tightdb_objc/build.sh package-watchos - cp tightdb_objc/build/watchos/realm-framework-watchos.zip . - sh tightdb_objc/build.sh package-watchos-swift - cp tightdb_objc/build/watchos/realm-swift-framework-watchos.zip . + sh build.sh package-watchos + cp build/watchos/realm-framework-watchos.zip .. + sh build.sh package-watchos-swift + cp build/watchos/realm-swift-framework-watchos.zip .. echo 'Packaging tvOS' - sh tightdb_objc/build.sh package-tvos - cp tightdb_objc/build/tvos/realm-framework-tvos.zip . - sh tightdb_objc/build.sh package-tvos-swift - cp tightdb_objc/build/tvos/realm-swift-framework-tvos.zip . + sh build.sh package-tvos + cp build/tvos/realm-framework-tvos.zip .. + sh build.sh package-tvos-swift + cp build/tvos/realm-swift-framework-tvos.zip .. echo 'Packaging examples' - sh tightdb_objc/build.sh package-examples - cp tightdb_objc/realm-examples.zip . + sh build.sh package-examples + cp realm-examples.zip .. echo 'Building final release packages' - sh tightdb_objc/build.sh package-release objc - sh tightdb_objc/build.sh package-release swift + sh build.sh package-release objc + sh build.sh package-release swift echo 'Testing packaged examples' - sh tightdb_objc/build.sh package-test-examples + sh build.sh package-test-examples ;; "github-release") @@ -1379,7 +1424,7 @@ EOF x.x.x Release notes (yyyy-MM-dd) ============================================================= -### API Breaking Changes +### Breaking Changes * None. diff --git a/Pods/Realm/core/librealm-ios.a b/Pods/Realm/core/librealmcore-ios.a similarity index 66% rename from Pods/Realm/core/librealm-ios.a rename to Pods/Realm/core/librealmcore-ios.a index d1955de..c8a041b 100644 Binary files a/Pods/Realm/core/librealm-ios.a and b/Pods/Realm/core/librealmcore-ios.a differ diff --git a/Pods/Realm/include/NSError+RLMSync.h b/Pods/Realm/include/NSError+RLMSync.h index ae08d8f..797db58 100644 --- a/Pods/Realm/include/NSError+RLMSync.h +++ b/Pods/Realm/include/NSError+RLMSync.h @@ -20,16 +20,19 @@ NS_ASSUME_NONNULL_BEGIN +@class RLMSyncErrorActionToken; + /// NSError category extension providing methods to get data out of Realm's /// "client reset" error. @interface NSError (RLMSync) /** - Given a Realm Object Server client reset error, return the block that can - be called to manually initiate the client reset process, or nil if the - error isn't a client reset error. + Given an appropriate Realm Object Server error, return the token that + can be passed into `+[RLMSyncSession immediatelyHandleError:]` to + immediately perform error clean-up work, or nil if the error isn't of + a type that provides a token. */ -- (nullable void(^)(void))rlmSync_clientResetBlock NS_REFINED_FOR_SWIFT; +- (nullable RLMSyncErrorActionToken *)rlmSync_errorActionToken NS_REFINED_FOR_SWIFT; /** Given a Realm Object Server client reset error, return the path where the diff --git a/Pods/Realm/include/RLMAccessor.h b/Pods/Realm/include/RLMAccessor.h index cc40523..59c625a 100644 --- a/Pods/Realm/include/RLMAccessor.h +++ b/Pods/Realm/include/RLMAccessor.h @@ -18,14 +18,7 @@ #import - -@class RLMObjectSchema, RLMProperty, RLMObjectBase, RLMProperty; - -#ifdef __cplusplus -typedef NSUInteger RLMCreationOptions; -#else -typedef NS_OPTIONS(NSUInteger, RLMCreationOptions); -#endif +@class RLMObjectSchema, RLMProperty, RLMObjectBase; NS_ASSUME_NONNULL_BEGIN @@ -45,7 +38,7 @@ FOUNDATION_EXTERN id __nullable RLMDynamicGet(RLMObjectBase *obj, RLMProperty *p FOUNDATION_EXTERN id __nullable RLMDynamicGetByName(RLMObjectBase *obj, NSString *propName, bool asList); // by property/column -void RLMDynamicSet(RLMObjectBase *obj, RLMProperty *prop, id val, RLMCreationOptions options); +void RLMDynamicSet(RLMObjectBase *obj, RLMProperty *prop, id val); // // Class modification diff --git a/Pods/Realm/include/RLMAccessor.hpp b/Pods/Realm/include/RLMAccessor.hpp new file mode 100644 index 0000000..93d7e68 --- /dev/null +++ b/Pods/Realm/include/RLMAccessor.hpp @@ -0,0 +1,116 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2017 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMAccessor.h" + +#import "object_accessor.hpp" + +#import "RLMUtil.hpp" + +@class RLMRealm; +class RLMClassInfo; +class RLMObservationInfo; + +// realm::util::Optional doesn't work because Objective-C types can't +// be members of unions with ARC, so this covers the subset of Optional that we +// actually need. +struct RLMOptionalId { + id value; + RLMOptionalId(id value) : value(value) { } + explicit operator bool() const noexcept { return value; } + id operator*() const noexcept { return value; } +}; + +class RLMAccessorContext { +public: + // Accessor context interface + RLMAccessorContext(RLMAccessorContext& parent, realm::Property const& property); + + id box(realm::List&&); + id box(realm::Results&&); + id box(realm::Object&&); + id box(realm::RowExpr); + + id box(bool v) { return @(v); } + id box(double v) { return @(v); } + id box(float v) { return @(v); } + id box(long long v) { return @(v); } + id box(realm::StringData v) { return RLMStringDataToNSString(v) ?: NSNull.null; } + id box(realm::BinaryData v) { return RLMBinaryDataToNSData(v) ?: NSNull.null; } + id box(realm::Timestamp v) { return RLMTimestampToNSDate(v) ?: NSNull.null; } + id box(realm::Mixed v) { return RLMMixedToObjc(v); } + + id box(realm::util::Optional v) { return v ? @(*v) : NSNull.null; } + id box(realm::util::Optional v) { return v ? @(*v) : NSNull.null; } + id box(realm::util::Optional v) { return v ? @(*v) : NSNull.null; } + id box(realm::util::Optional v) { return v ? @(*v) : NSNull.null; } + + void will_change(realm::Row const&, realm::Property const&); + void will_change(realm::Object& obj, realm::Property const& prop) { will_change(obj.row(), prop); } + void did_change(); + + RLMOptionalId value_for_property(id dict, std::string const&, size_t prop_index); + RLMOptionalId default_value_for_property(realm::ObjectSchema const&, + std::string const& prop); + + bool is_same_list(realm::List const& list, id v) const noexcept; + + template + void enumerate_list(__unsafe_unretained const id v, Func&& func) { + for (id value in v) { + func(value); + } + } + + template + T unbox(id v, bool create = false, bool update = false); + + bool is_null(id v) { return v == NSNull.null; } + id null_value() { return NSNull.null; } + id no_value() { return nil; } + bool allow_missing(id v) { return [v isKindOfClass:[NSArray class]]; } + + std::string print(id obj) { return [obj description].UTF8String; } + + // Internal API + RLMAccessorContext(RLMObjectBase *parentObject, const realm::Property *property = nullptr); + RLMAccessorContext(RLMRealm *realm, RLMClassInfo& info, bool promote=true); + + // The property currently being accessed; needed for KVO things for boxing + // List and Results + RLMProperty *currentProperty; + +private: + __unsafe_unretained RLMRealm *const _realm; + RLMClassInfo& _info; + // If true, promote unmanaged RLMObjects passed to box() with create=true + // rather than copying them + bool _promote_existing = true; + // Parent object of the thing currently being processed, for KVO purposes + __unsafe_unretained RLMObjectBase *const _parentObject = nil; + + // Cached default values dictionary to avoid having to call the class method + // for every property + NSDictionary *_defaultValues; + + RLMObservationInfo *_observationInfo = nullptr; + NSString *_kvoPropertyName = nil; + + id defaultValue(NSString *key); + id propertyValue(id obj, size_t propIndex, __unsafe_unretained RLMProperty *const prop); +}; diff --git a/Pods/Realm/include/RLMAnalytics.hpp b/Pods/Realm/include/RLMAnalytics.hpp index 76bc429..ccb4ea2 100644 --- a/Pods/Realm/include/RLMAnalytics.hpp +++ b/Pods/Realm/include/RLMAnalytics.hpp @@ -48,7 +48,7 @@ // - What version of OS X it's running on (in case Xcode aggressively drops // support for older versions again, we need to know what we need to support). // - The minimum iOS/OS X version that the application is targeting (again, to -// help us decide what versions we need to support). +// help us decide what versions we need to support). // - An anonymous MAC address and bundle ID to aggregate the other information on. // - What version of Swift is being used (if applicable). diff --git a/Pods/Realm/include/RLMArray.h b/Pods/Realm/include/RLMArray.h index c6c4e96..3ef38eb 100644 --- a/Pods/Realm/include/RLMArray.h +++ b/Pods/Realm/include/RLMArray.h @@ -16,13 +16,11 @@ // //////////////////////////////////////////////////////////////////////////// -#import - #import NS_ASSUME_NONNULL_BEGIN -@class RLMObject, RLMRealm, RLMResults, RLMNotificationToken; +@class RLMObject, RLMResults; /** `RLMArray` is the container type in Realm used to define to-many relationships. @@ -57,7 +55,7 @@ NS_ASSUME_NONNULL_BEGIN object. Instead, you can call the mutation methods on the `RLMArray` directly. */ -@interface RLMArray : NSObject +@interface RLMArray : NSObject #pragma mark - Properties @@ -67,9 +65,21 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly, assign) NSUInteger count; /** - The class name (i.e. type) of the `RLMObject`s contained in the array. + The type of the objects in the array. + */ +@property (nonatomic, readonly, assign) RLMPropertyType type; + +/** + Indicates whether the objects in the collection can be `nil`. + */ +@property (nonatomic, readonly, getter = isOptional) BOOL optional; + +/** + The class name of the objects contained in the array. + + Will be `nil` if `type` is not RLMPropertyTypeObject. */ -@property (nonatomic, readonly, copy) NSString *objectClassName; +@property (nonatomic, readonly, copy, nullable) NSString *objectClassName; /** The Realm which manages the array. Returns `nil` for unmanaged arrays. @@ -88,7 +98,7 @@ NS_ASSUME_NONNULL_BEGIN @param index The index to look up. - @return An `RLMObject` of the type contained in the array. + @return An object of the type contained in the array. */ - (RLMObjectType)objectAtIndex:(NSUInteger)index; @@ -97,7 +107,7 @@ NS_ASSUME_NONNULL_BEGIN Returns `nil` if called on an empty array. - @return An `RLMObject` of the type contained in the array. + @return An object of the type contained in the array. */ - (nullable RLMObjectType)firstObject; @@ -106,7 +116,7 @@ NS_ASSUME_NONNULL_BEGIN Returns `nil` if called on an empty array. - @return An `RLMObject` of the type contained in the array. + @return An object of the type contained in the array. */ - (nullable RLMObjectType)lastObject; @@ -119,7 +129,7 @@ NS_ASSUME_NONNULL_BEGIN @warning This method may only be called during a write transaction. - @param object An `RLMObject` of the type contained in the array. + @param object An object of the type contained in the array. */ - (void)addObject:(RLMObjectType)object; @@ -140,7 +150,7 @@ NS_ASSUME_NONNULL_BEGIN @warning This method may only be called during a write transaction. - @param anObject An `RLMObject` of the type contained in the array. + @param anObject An object of the type contained in the array. @param index The index at which to insert the object. */ - (void)insertObject:(RLMObjectType)anObject atIndex:(NSUInteger)index; @@ -159,6 +169,8 @@ NS_ASSUME_NONNULL_BEGIN /** Removes the last object in the array. + This is a no-op if the array is already empty. + @warning This method may only be called during a write transaction. */ - (void)removeLastObject; @@ -269,17 +281,6 @@ NS_ASSUME_NONNULL_BEGIN */ - (RLMResults *)sortedResultsUsingKeyPath:(NSString *)keyPath ascending:(BOOL)ascending; -/** - Returns a sorted `RLMResults` from the array. - - @param property The property name to sort by. - @param ascending The direction to sort in. - - @return An `RLMResults` sorted by the specified property. - */ -- (RLMResults *)sortedResultsUsingProperty:(NSString *)property ascending:(BOOL)ascending - __deprecated_msg("Use `-sortedResultsUsingKeyPath:ascending:`"); - /** Returns a sorted `RLMResults` from the array. @@ -342,7 +343,7 @@ NS_ASSUME_NONNULL_BEGIN // end of run loop execution context You must retain the returned token for as long as you want updates to continue - to be sent to the block. To stop receiving updates, call `-stop` on the token. + to be sent to the block. To stop receiving updates, call `-invalidate` on the token. @warning This method cannot be called during a write transaction, or when the containing Realm is read-only. @@ -355,17 +356,76 @@ NS_ASSUME_NONNULL_BEGIN RLMCollectionChange *__nullable changes, NSError *__nullable error))block __attribute__((warn_unused_result)); +#pragma mark - Aggregating Property Values + +/** + Returns the minimum (lowest) value of the given property among all the objects in the array. + + NSNumber *min = [object.arrayProperty minOfProperty:@"age"]; + + @warning You cannot use this method on `RLMObject`, `RLMArray`, and `NSData` properties. + + @param property The property whose minimum value is desired. Only properties of + types `int`, `float`, `double`, and `NSDate` are supported. + + @return The minimum value of the property, or `nil` if the array is empty. + */ +- (nullable id)minOfProperty:(NSString *)property; + +/** + Returns the maximum (highest) value of the given property among all the objects in the array. + + NSNumber *max = [object.arrayProperty maxOfProperty:@"age"]; + + @warning You cannot use this method on `RLMObject`, `RLMArray`, and `NSData` properties. + + @param property The property whose maximum value is desired. Only properties of + types `int`, `float`, `double`, and `NSDate` are supported. + + @return The maximum value of the property, or `nil` if the array is empty. + */ +- (nullable id)maxOfProperty:(NSString *)property; + +/** + Returns the sum of the values of a given property over all the objects in the array. + + NSNumber *sum = [object.arrayProperty sumOfProperty:@"age"]; + + @warning You cannot use this method on `RLMObject`, `RLMArray`, and `NSData` properties. + + @param property The property whose values should be summed. Only properties of + types `int`, `float`, and `double` are supported. + + @return The sum of the given property. + */ +- (NSNumber *)sumOfProperty:(NSString *)property; + +/** + Returns the average value of a given property over the objects in the array. + + NSNumber *average = [object.arrayProperty averageOfProperty:@"age"]; + + @warning You cannot use this method on `RLMObject`, `RLMArray`, and `NSData` properties. + + @param property The property whose average value should be calculated. Only + properties of types `int`, `float`, and `double` are supported. + + @return The average value of the given property, or `nil` if the array is empty. + */ +- (nullable NSNumber *)averageOfProperty:(NSString *)property; + + #pragma mark - Unavailable Methods /** `-[RLMArray init]` is not available because `RLMArray`s cannot be created directly. - `RLMArray` properties on `RLMObject`s are lazily created when accessed, or can be obtained by querying a Realm. + `RLMArray` properties on `RLMObject`s are lazily created when accessed. */ - (instancetype)init __attribute__((unavailable("RLMArrays cannot be created directly"))); /** `+[RLMArray new]` is not available because `RLMArray`s cannot be created directly. - `RLMArray` properties on `RLMObject`s are lazily created when accessed, or can be obtained by querying a Realm. + `RLMArray` properties on `RLMObject`s are lazily created when accessed. */ + (instancetype)new __attribute__((unavailable("RLMArrays cannot be created directly"))); diff --git a/Pods/Realm/include/RLMArray_Private.h b/Pods/Realm/include/RLMArray_Private.h index 5e15e2d..02b908a 100644 --- a/Pods/Realm/include/RLMArray_Private.h +++ b/Pods/Realm/include/RLMArray_Private.h @@ -17,12 +17,16 @@ //////////////////////////////////////////////////////////////////////////// #import +#import NS_ASSUME_NONNULL_BEGIN @interface RLMArray () - (instancetype)initWithObjectClassName:(NSString *)objectClassName; +- (instancetype)initWithObjectType:(RLMPropertyType)type optional:(BOOL)optional; - (NSString *)descriptionWithMaxDepth:(NSUInteger)depth; @end +void RLMArrayValidateMatchingObjectType(RLMArray *array, id value); + NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMArray_Private.hpp b/Pods/Realm/include/RLMArray_Private.hpp index 5db9abe..08a4487 100644 --- a/Pods/Realm/include/RLMArray_Private.hpp +++ b/Pods/Realm/include/RLMArray_Private.hpp @@ -20,9 +20,10 @@ #import "RLMCollection_Private.hpp" -#import +#import "RLMResults_Private.hpp" #import +#import namespace realm { class Results; @@ -35,6 +36,8 @@ class RLMObservationInfo; @interface RLMArray () { @protected NSString *_objectClassName; + RLMPropertyType _type; + BOOL _optional; @public // The name of the property which this RLMArray represents NSString *_key; @@ -42,11 +45,14 @@ class RLMObservationInfo; } @end -// -// LinkView backed RLMArray subclass -// -@interface RLMArrayLinkView : RLMArray +@interface RLMManagedArray : RLMArray - (instancetype)initWithParent:(RLMObjectBase *)parentObject property:(RLMProperty *)property; +- (RLMManagedArray *)initWithList:(realm::List)list + realm:(__unsafe_unretained RLMRealm *const)realm + parentInfo:(RLMClassInfo *)parentInfo + property:(__unsafe_unretained RLMProperty *const)property; + +- (bool)isBackedByList:(realm::List const&)list; // deletes all objects in the RLMArray from their containing realms - (void)deleteObjectsFromRealm; @@ -63,8 +69,5 @@ void RLMEnsureArrayObservationInfo(std::unique_ptr& info, // RLMResults private methods // @interface RLMResults () -+ (instancetype)resultsWithObjectInfo:(RLMClassInfo&)info - results:(realm::Results)results; - - (void)deleteObjectsFromRealm; @end diff --git a/Pods/Realm/include/RLMAuthResponseModel.h b/Pods/Realm/include/RLMAuthResponseModel.h deleted file mode 100644 index 0cbf68e..0000000 --- a/Pods/Realm/include/RLMAuthResponseModel.h +++ /dev/null @@ -1,52 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#import - -/** - An internal class representing a valid JSON response to an auth request. - - ``` - { - "access_token": { ... } // (optional), - "refresh_token": { ... } // (optional) - } - ``` - */ -@class RLMTokenModel; - -NS_ASSUME_NONNULL_BEGIN - -@interface RLMAuthResponseModel : NSObject - -@property (nonatomic, readonly, nullable) RLMTokenModel *accessToken; -@property (nonatomic, readonly, nullable) RLMTokenModel *refreshToken; - -- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary - requireAccessToken:(BOOL)requireAccessToken - requireRefreshToken:(BOOL)requireRefreshToken; - -/// :nodoc: -- (instancetype)init __attribute__((unavailable("RLMTokenModel cannot be created directly"))); - -/// :nodoc: -+ (instancetype)new __attribute__((unavailable("RLMTokenModel cannot be created directly"))); - -@end - -NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMClassInfo.hpp b/Pods/Realm/include/RLMClassInfo.hpp index 5c1b268..7d1d23e 100644 --- a/Pods/Realm/include/RLMClassInfo.hpp +++ b/Pods/Realm/include/RLMClassInfo.hpp @@ -80,6 +80,9 @@ class RLMClassInfo { // Get the info for the target of the link at the given property index. RLMClassInfo &linkTargetType(size_t propertyIndex); + // Get the info for the target of the given property + RLMClassInfo &linkTargetType(realm::Property const& property); + void releaseTable() { m_table = nullptr; } private: @@ -92,7 +95,9 @@ class RLMSchemaInfo { using impl = std::unordered_map; public: RLMSchemaInfo() = default; - RLMSchemaInfo(RLMRealm *realm, RLMSchema *rlmSchema, realm::Schema const& schema); + RLMSchemaInfo(RLMRealm *realm); + + RLMSchemaInfo clone(realm::Schema const& source_schema, RLMRealm *target_realm); // Look up by name, throwing if it's not present RLMClassInfo& operator[](NSString *name); diff --git a/Pods/Realm/include/RLMCollection.h b/Pods/Realm/include/RLMCollection.h index 4a66053..9d3fa7e 100644 --- a/Pods/Realm/include/RLMCollection.h +++ b/Pods/Realm/include/RLMCollection.h @@ -18,15 +18,16 @@ #import -#import "RLMThreadSafeReference.h" +#import NS_ASSUME_NONNULL_BEGIN -@class RLMRealm, RLMResults, RLMObject, RLMSortDescriptor, RLMNotificationToken, RLMCollectionChange; +@class RLMRealm, RLMResults, RLMSortDescriptor, RLMNotificationToken, RLMCollectionChange; +typedef NS_ENUM(int32_t, RLMPropertyType); /** - A homogenous collection of `RLMObject` instances. Examples of conforming types include `RLMArray`, - `RLMResults`, and `RLMLinkingObjects`. + A homogenous collection of Realm-managed objects. Examples of conforming types + include `RLMArray`, `RLMResults`, and `RLMLinkingObjects`. */ @protocol RLMCollection @@ -40,9 +41,21 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly, assign) NSUInteger count; /** - The class name (i.e. type) of the `RLMObject`s contained in the collection. + The type of the objects in the collection. */ -@property (nonatomic, readonly, copy) NSString *objectClassName; +@property (nonatomic, readonly, assign) RLMPropertyType type; + +/** + Indicates whether the objects in the collection can be `nil`. + */ +@property (nonatomic, readonly, getter = isOptional) BOOL optional; + +/** + The class name of the objects contained in the collection. + + Will be `nil` if `type` is not RLMPropertyTypeObject. + */ +@property (nonatomic, readonly, copy, nullable) NSString *objectClassName; /** The Realm which manages the collection, or `nil` for unmanaged collections. @@ -56,7 +69,7 @@ NS_ASSUME_NONNULL_BEGIN @param index The index to look up. - @return An `RLMObject` of the type contained in the collection. + @return An object of the type contained in the collection. */ - (id)objectAtIndex:(NSUInteger)index; @@ -65,7 +78,7 @@ NS_ASSUME_NONNULL_BEGIN Returns `nil` if called on an empty collection. - @return An `RLMObject` of the type contained in the collection. + @return An object of the type contained in the collection. */ - (nullable id)firstObject; @@ -74,7 +87,7 @@ NS_ASSUME_NONNULL_BEGIN Returns `nil` if called on an empty collection. - @return An `RLMObject` of the type contained in the collection. + @return An object of the type contained in the collection. */ - (nullable id)lastObject; @@ -87,7 +100,7 @@ NS_ASSUME_NONNULL_BEGIN @param object An object (of the same type as returned from the `objectClassName` selector). */ -- (NSUInteger)indexOfObject:(RLMObject *)object; +- (NSUInteger)indexOfObject:(id)object; /** Returns the index of the first object in the collection matching the predicate. @@ -141,17 +154,6 @@ NS_ASSUME_NONNULL_BEGIN */ - (RLMResults *)sortedResultsUsingKeyPath:(NSString *)keyPath ascending:(BOOL)ascending; -/** - Returns a sorted `RLMResults` from the collection. - - @param property The property name to sort by. - @param ascending The direction to sort in. - - @return An `RLMResults` sorted by the specified property. - */ -- (RLMResults *)sortedResultsUsingProperty:(NSString *)property ascending:(BOOL)ascending - __deprecated_msg("Use `-sortedResultsUsingKeyPath:ascending:`"); - /** Returns a sorted `RLMResults` from the collection. @@ -234,7 +236,7 @@ NS_ASSUME_NONNULL_BEGIN // end of run loop execution context You must retain the returned token for as long as you want updates to continue - to be sent to the block. To stop receiving updates, call `-stop` on the token. + to be sent to the block. To stop receiving updates, call `-invalidate` on the token. @warning This method cannot be called during a write transaction, or when the containing Realm is read-only. @@ -246,6 +248,66 @@ NS_ASSUME_NONNULL_BEGIN RLMCollectionChange *__nullable change, NSError *__nullable error))block __attribute__((warn_unused_result)); +#pragma mark - Aggregating Property Values + +/** + Returns the minimum (lowest) value of the given property among all the objects + in the collection. + + NSNumber *min = [results minOfProperty:@"age"]; + + @warning You cannot use this method on `RLMObject`, `RLMArray`, and `NSData` properties. + + @param property The property whose minimum value is desired. Only properties of + types `int`, `float`, `double`, and `NSDate` are supported. + + @return The minimum value of the property, or `nil` if the Results are empty. + */ +- (nullable id)minOfProperty:(NSString *)property; + +/** + Returns the maximum (highest) value of the given property among all the objects + in the collection. + + NSNumber *max = [results maxOfProperty:@"age"]; + + @warning You cannot use this method on `RLMObject`, `RLMArray`, and `NSData` properties. + + @param property The property whose maximum value is desired. Only properties of + types `int`, `float`, `double`, and `NSDate` are supported. + + @return The maximum value of the property, or `nil` if the Results are empty. + */ +- (nullable id)maxOfProperty:(NSString *)property; + +/** + Returns the sum of the values of a given property over all the objects in the collection. + + NSNumber *sum = [results sumOfProperty:@"age"]; + + @warning You cannot use this method on `RLMObject`, `RLMArray`, and `NSData` properties. + + @param property The property whose values should be summed. Only properties of + types `int`, `float`, and `double` are supported. + + @return The sum of the given property. + */ +- (NSNumber *)sumOfProperty:(NSString *)property; + +/** + Returns the average value of a given property over the objects in the collection. + + NSNumber *average = [results averageOfProperty:@"age"]; + + @warning You cannot use this method on `RLMObject`, `RLMArray`, and `NSData` properties. + + @param property The property whose average value should be calculated. Only + properties of types `int`, `float`, and `double` are supported. + + @return The average value of the given property, or `nil` if the Results are empty. + */ +- (nullable NSNumber *)averageOfProperty:(NSString *)property; + @end /** @@ -253,7 +315,7 @@ NS_ASSUME_NONNULL_BEGIN `sortedResultsUsingDescriptors:`. It is similar to `NSSortDescriptor`, but supports only the subset of functionality which can be efficiently run by Realm's query engine. - + `RLMSortDescriptor` instances are immutable. */ @interface RLMSortDescriptor : NSObject @@ -282,19 +344,6 @@ NS_ASSUME_NONNULL_BEGIN */ - (instancetype)reversedSortDescriptor; -#pragma mark - Deprecated - -/** - The name of the property which the sort descriptor orders results by. - */ -@property (nonatomic, readonly) NSString *property __deprecated_msg("Use `-keyPath`"); - -/** - Returns a new sort descriptor for the given property name and sort direction. - */ -+ (instancetype)sortDescriptorWithProperty:(NSString *)propertyName ascending:(BOOL)ascending - __deprecated_msg("Use `+sortDescriptorWithKeyPath:ascending:`"); - @end /** @@ -328,11 +377,11 @@ NS_ASSUME_NONNULL_BEGIN /** The indices in the new version of the collection which were modified. - + For `RLMResults`, this means that one or more of the properties of the object at that index were modified (or an object linked to by that object was modified). - + For `RLMArray`, the array itself being modified to contain a different object at that index will also be reported as a modification. */ diff --git a/Pods/Realm/include/RLMSyncPermissionOffer_Private.h b/Pods/Realm/include/RLMCollection_Private.h similarity index 54% rename from Pods/Realm/include/RLMSyncPermissionOffer_Private.h rename to Pods/Realm/include/RLMCollection_Private.h index 3d7b72e..4c3f882 100644 --- a/Pods/Realm/include/RLMSyncPermissionOffer_Private.h +++ b/Pods/Realm/include/RLMCollection_Private.h @@ -16,27 +16,11 @@ // //////////////////////////////////////////////////////////////////////////// -#import "RLMSyncPermissionOffer.h" +#import -NS_ASSUME_NONNULL_BEGIN +#import -@interface RLMSyncPermissionOffer() +@protocol RLMFastEnumerable; -@property (readwrite) NSString *id; -@property (readwrite) NSDate *createdAt; -@property (readwrite) NSDate *updatedAt; -@property (nullable, readwrite) NSNumber *statusCode; -@property (nullable, readwrite) NSString *statusMessage; - -@property (nullable, readwrite) NSString *token; -@property (readwrite) NSString *realmUrl; - -@property (readwrite) BOOL mayRead; -@property (readwrite) BOOL mayWrite; -@property (readwrite) BOOL mayManage; - -@property (nullable, readwrite) NSDate *expiresAt; - -@end - -NS_ASSUME_NONNULL_END +void RLMCollectionSetValueForKey(id collection, NSString *key, id value); +FOUNDATION_EXTERN NSString *RLMDescriptionWithMaxDepth(NSString *name, id collection, NSUInteger depth); diff --git a/Pods/Realm/include/RLMCollection_Private.hpp b/Pods/Realm/include/RLMCollection_Private.hpp index 9f58c97..908c886 100644 --- a/Pods/Realm/include/RLMCollection_Private.hpp +++ b/Pods/Realm/include/RLMCollection_Private.hpp @@ -16,9 +16,9 @@ // //////////////////////////////////////////////////////////////////////////// -#import +#import -#import +#import namespace realm { class List; @@ -28,22 +28,29 @@ namespace realm { struct NotificationToken; } class RLMClassInfo; +@class RLMFastEnumerator; @protocol RLMFastEnumerable @property (nonatomic, readonly) RLMRealm *realm; @property (nonatomic, readonly) RLMClassInfo *objectInfo; @property (nonatomic, readonly) NSUInteger count; -- (NSUInteger)indexInSource:(NSUInteger)index; - (realm::TableView)tableView; +- (RLMFastEnumerator *)fastEnumerator; @end // An object which encapulates the shared logic for fast-enumerating RLMArray // and RLMResults, and has a buffer to store strong references to the current // set of enumerated items @interface RLMFastEnumerator : NSObject -- (instancetype)initWithCollection:(id)collection - objectSchema:(RLMClassInfo&)objectSchema; +- (instancetype)initWithList:(realm::List&)list + collection:(id)collection + realm:(RLMRealm *)realm + classInfo:(RLMClassInfo&)info; +- (instancetype)initWithResults:(realm::Results&)results + collection:(id)collection + realm:(RLMRealm *)realm + classInfo:(RLMClassInfo&)info; // Detach this enumerator from the source collection. Must be called before the // source collection is changed. @@ -52,6 +59,7 @@ class RLMClassInfo; - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state count:(NSUInteger)len; @end +NSUInteger RLMFastEnumerate(NSFastEnumerationState *state, NSUInteger len, id collection); @interface RLMNotificationToken () - (void)suppressNextNotification; @@ -72,6 +80,8 @@ RLMNotificationToken *RLMAddNotificationBlock(id objcCollection, void (^block)(id, RLMCollectionChange *, NSError *), bool suppressInitialChange=false); -NSArray *RLMCollectionValueForKey(id collection, NSString *key); -void RLMCollectionSetValueForKey(id collection, NSString *key, id value); -NSString *RLMDescriptionWithMaxDepth(NSString *name, id collection, NSUInteger depth); +template +NSArray *RLMCollectionValueForKey(Collection& collection, NSString *key, + RLMRealm *realm, RLMClassInfo& info); + +std::vector> RLMSortDescriptorsToKeypathArray(NSArray *properties); diff --git a/Pods/Realm/include/RLMConstants.h b/Pods/Realm/include/RLMConstants.h index bf3b2e7..f73fe83 100644 --- a/Pods/Realm/include/RLMConstants.h +++ b/Pods/Realm/include/RLMConstants.h @@ -47,7 +47,6 @@ NS_ASSUME_NONNULL_BEGIN For more information, see [Realm Models](https://realm.io/docs/objc/latest/#models). */ -// Make sure numbers match those in typedef NS_ENUM(int32_t, RLMPropertyType) { #pragma mark - Primitive types @@ -57,34 +56,32 @@ typedef NS_ENUM(int32_t, RLMPropertyType) { /** Booleans: `BOOL`, `bool`, `Bool` (Swift) */ RLMPropertyTypeBool = 1, /** Floating-point numbers: `float`, `Float` (Swift) */ - RLMPropertyTypeFloat = 9, + RLMPropertyTypeFloat = 5, /** Double-precision floating-point numbers: `double`, `Double` (Swift) */ - RLMPropertyTypeDouble = 10, + RLMPropertyTypeDouble = 6, #pragma mark - Object types /** Strings: `NSString`, `String` (Swift) */ RLMPropertyTypeString = 2, /** Binary data: `NSData` */ - RLMPropertyTypeData = 4, - /** + RLMPropertyTypeData = 3, + /** Any object: `id`. - - This property type is no longer supported for new models. However, old models with any-typed properties are still - supported for migration purposes. + + This property type is no longer supported for new models. However, old files + with any-typed properties are still supported for migration purposes. */ - RLMPropertyTypeAny = 6, + RLMPropertyTypeAny = 9, /** Dates: `NSDate` */ - RLMPropertyTypeDate = 8, + RLMPropertyTypeDate = 4, -#pragma mark - Array/Linked object types +#pragma mark - Linked object types /** Realm model objects. See [Realm Models](https://realm.io/docs/objc/latest/#models) for more information. */ - RLMPropertyTypeObject = 12, - /** Realm arrays. See [Realm Models](https://realm.io/docs/objc/latest/#models) for more information. */ - RLMPropertyTypeArray = 13, + RLMPropertyTypeObject = 7, /** Realm linking objects. See [Realm Models](https://realm.io/docs/objc/latest/#models) for more information. */ - RLMPropertyTypeLinkingObjects = 14, + RLMPropertyTypeLinkingObjects = 8, }; /** An error domain identifying Realm-specific errors. */ @@ -104,9 +101,9 @@ typedef RLM_ERROR_ENUM(NSInteger, RLMError, RLMErrorDomain) { /** Denotes a file I/O error that occurred when trying to open a Realm. */ RLMErrorFileAccess = 2, - /** + /** Denotes a file permission error that ocurred when trying to open a Realm. - + This error can occur if the user does not have permission to open or create the specified file in the specified access mode when opening a Realm. */ @@ -117,24 +114,24 @@ typedef RLM_ERROR_ENUM(NSInteger, RLMError, RLMErrorDomain) { /** Denotes an error that occurs if a file could not be found. - + This error may occur if a Realm file could not be found on disk when trying to open a Realm as read-only, or if the directory part of the specified path was not found when trying to write a copy. */ RLMErrorFileNotFound = 5, - /** + /** Denotes an error that occurs if a file format upgrade is required to open the file, but upgrades were explicitly disabled. */ RLMErrorFileFormatUpgradeRequired = 6, - /** + /** Denotes an error that occurs if the database file is currently open in another process which cannot share with the current process due to an architecture mismatch. - + This error may occur if trying to share a Realm file between an i386 (32-bit) iOS Simulator and the Realm Browser application. In this case, please use the 64-bit version of the iOS Simulator. @@ -146,6 +143,15 @@ typedef RLM_ERROR_ENUM(NSInteger, RLMError, RLMErrorDomain) { /** Denotes an error that occurs if there is a schema version mismatch, so that a migration is required. */ RLMErrorSchemaMismatch = 10, + + /** Denotes an error that occurs when attempting to open an incompatible synchronized Realm file. + + This error occurs when the Realm file was created with an older version of Realm and an automatic migration + to the current version is not possible. When such an error occurs, the original file is moved to a backup + location, and future attempts to open the synchronized Realm will result in a new file being created. + If you wish to migrate any data from the backup Realm, you can open it using the provided Realm configuration. + */ + RLMErrorIncompatibleSyncedFile = 11, }; #pragma mark - Constants @@ -184,6 +190,11 @@ RLM_EXTENSIBLE_STRING_ENUM_CASE_SWIFT_NAME(RLMRealmRefreshRequiredNotification, extern RLMNotification const RLMRealmDidChangeNotification RLM_EXTENSIBLE_STRING_ENUM_CASE_SWIFT_NAME(RLMRealmDidChangeNotification, DidChange); +#pragma mark - Error keys + +/** Key to identify the associated backup Realm configuration in an error's `userInfo` dictionary */ +extern NSString * const RLMBackupRealmConfigurationErrorKey; + #pragma mark - Other Constants /** The schema version used for uninitialized Realms */ diff --git a/Pods/Realm/include/RLMJSONModels.h b/Pods/Realm/include/RLMJSONModels.h new file mode 100644 index 0000000..9cc650b --- /dev/null +++ b/Pods/Realm/include/RLMJSONModels.h @@ -0,0 +1,103 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2017 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +#import "RLMSyncUtil_Private.h" + +NS_ASSUME_NONNULL_BEGIN + +@class RLMTokenDataModel, RLMSyncUserAccountInfo; + +#pragma mark - RLMTokenModel + +@interface RLMTokenModel : NSObject RLM_SYNC_UNINITIALIZABLE + +@property (nonatomic, readonly) NSString *token; +@property (nonatomic, nullable, readonly) NSString *path; +@property (nonatomic, readonly) RLMTokenDataModel *tokenData; + +- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary; + +@end + +#pragma mark - RLMTokenDataModel + +@interface RLMTokenDataModel : NSObject RLM_SYNC_UNINITIALIZABLE + +@property (nonatomic, readonly) NSString *identity; +@property (nonatomic, nullable, readonly) NSString *appID; +@property (nonatomic, nullable, readonly) NSString *path; +@property (nonatomic, readonly) NSTimeInterval expires; +@property (nonatomic, readonly) BOOL isAdmin; +//@property (nonatomic, readonly) NSArray *access; + +- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary; + +@end + +#pragma mark - RLMAuthResponseModel + +/** + An internal class representing a valid JSON response to an auth request. + + ``` + { + "access_token": { ... } // (optional), + "refresh_token": { ... } // (optional) + } + ``` + */ +@interface RLMAuthResponseModel : NSObject RLM_SYNC_UNINITIALIZABLE + +@property (nonatomic, readonly, nullable) RLMTokenModel *accessToken; +@property (nonatomic, readonly, nullable) RLMTokenModel *refreshToken; + +- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary + requireAccessToken:(BOOL)requireAccessToken + requireRefreshToken:(BOOL)requireRefreshToken; + +@end + +#pragma mark - RLMUserInfoResponseModel + +@interface RLMUserResponseModel : NSObject RLM_SYNC_UNINITIALIZABLE + +@property (nonatomic, readonly) NSString *identity; +@property (nonatomic, readonly) NSArray *accounts; +@property (nonatomic, readonly) NSDictionary *metadata; +@property (nonatomic, readonly) BOOL isAdmin; + +- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary; + +@end + +#pragma mark - RLMSyncErrorResponseModel + +@interface RLMSyncErrorResponseModel : NSObject RLM_SYNC_UNINITIALIZABLE + +@property (nonatomic, readonly) NSInteger status; +@property (nonatomic, readonly) NSInteger code; +@property (nullable, nonatomic, readonly) NSString *title; +@property (nullable, nonatomic, readonly) NSString *hint; + +- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMMigration.h b/Pods/Realm/include/RLMMigration.h index d3da9ff..e910c42 100644 --- a/Pods/Realm/include/RLMMigration.h +++ b/Pods/Realm/include/RLMMigration.h @@ -25,11 +25,11 @@ NS_ASSUME_NONNULL_BEGIN @class RLMObject; /** - A block type which provides both the old and new versions of an object in the Realm. Object + A block type which provides both the old and new versions of an object in the Realm. Object properties can only be accessed using keyed subscripting. - + @see `-[RLMMigration enumerateObjects:block:]` - + @param oldObject The object from the original Realm (read-only). @param newObject The object from the migrated Realm (read-write). */ @@ -37,7 +37,7 @@ typedef void (^RLMObjectMigrationBlock)(RLMObject * __nullable oldObject, RLMObj /** `RLMMigration` instances encapsulate information intended to facilitate a schema migration. - + A `RLMMigration` instance is passed into a user-defined `RLMMigrationBlock` block when updating the version of a Realm. This instance provides access to the old and new database schemas, the objects in the Realm, and provides functionality for modifying the Realm during the migration. @@ -75,8 +75,8 @@ typedef void (^RLMObjectMigrationBlock)(RLMObject * __nullable oldObject, RLMObj /** Creates and returns an `RLMObject` instance of type `className` in the Realm being migrated. - - The `value` argument is used to populate the object. It can be a key-value coding compliant object, an array or + + The `value` argument is used to populate the object. It can be a key-value coding compliant object, an array or dictionary returned from the methods in `NSJSONSerialization`, or an array containing one element for each managed property. An exception will be thrown if any required properties are not present and those properties were not defined with default values. diff --git a/Pods/Realm/include/RLMNetworkClient.h b/Pods/Realm/include/RLMNetworkClient.h index bde6ecf..09b43ee 100644 --- a/Pods/Realm/include/RLMNetworkClient.h +++ b/Pods/Realm/include/RLMNetworkClient.h @@ -20,36 +20,51 @@ #import "RLMSyncUtil_Private.h" -/** - An enum describing all possible endpoints on the Realm Object Server. - */ -typedef NS_ENUM(NSUInteger, RLMServerEndpoint) { - RLMServerEndpointAuth, - RLMServerEndpointLogout, - RLMServerEndpointAddCredentials, - RLMServerEndpointRemoveCredentials, -}; +NS_ASSUME_NONNULL_BEGIN + +@interface RLMNetworkRequestOptions : NSObject +@property (nonatomic, copy, nullable) NSString *authorizationHeaderName; +@property (nonatomic, copy, nullable) NSDictionary *customHeaders; +@end + +/// An abstract class representing a server endpoint. +@interface RLMSyncServerEndpoint : NSObject RLM_SYNC_UNINITIALIZABLE ++ (instancetype)endpoint; + ++ (void)sendRequestToServer:(NSURL *)serverURL + JSON:(NSDictionary *)jsonDictionary + options:(nullable RLMNetworkRequestOptions *)options + completion:(void (^)(NSError *))completionBlock; +@end + +/// The authentication endpoint. +@interface RLMSyncAuthEndpoint : RLMSyncServerEndpoint RLM_SYNC_UNINITIALIZABLE +@end + +/// The password change endpoint. +@interface RLMSyncChangePasswordEndpoint : RLMSyncServerEndpoint RLM_SYNC_UNINITIALIZABLE +@end + +@interface RLMSyncUpdateAccountEndpoint : RLMSyncServerEndpoint RLM_SYNC_UNINITIALIZABLE +@end + +/// The get user info endpoint. +@interface RLMSyncGetUserInfoEndpoint : RLMSyncServerEndpoint RLM_SYNC_UNINITIALIZABLE +@end /** A simple Realm Object Server network client that wraps `NSURLSession`. */ @interface RLMNetworkClient : NSObject -NS_ASSUME_NONNULL_BEGIN +// Set the timeout in seconds for requests which do not take an explicit timeout. ++ (void)setDefaultTimeout:(NSTimeInterval)timeout; -+ (void)postRequestToEndpoint:(RLMServerEndpoint)endpoint - server:(NSURL *)serverURL - JSON:(NSDictionary *)jsonDictionary - completion:(RLMSyncCompletionBlock)completionBlock; - -/** - Post some JSON data to the authentication server, and asynchronously call a completion block with a JSON response - and/or error. - */ -+ (void)postRequestToEndpoint:(RLMServerEndpoint)endpoint ++ (void)sendRequestToEndpoint:(RLMSyncServerEndpoint *)endpoint server:(NSURL *)serverURL JSON:(NSDictionary *)jsonDictionary timeout:(NSTimeInterval)timeout + options:(nullable RLMNetworkRequestOptions *)options completion:(RLMSyncCompletionBlock)completionBlock; NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMObject.h b/Pods/Realm/include/RLMObject.h index c803e35..2b55a3e 100644 --- a/Pods/Realm/include/RLMObject.h +++ b/Pods/Realm/include/RLMObject.h @@ -28,7 +28,7 @@ NS_ASSUME_NONNULL_BEGIN @class RLMPropertyChange; @class RLMPropertyDescriptor; @class RLMRealm; -@class RLMResults; +@class RLMResults; /** `RLMObject` is a base class for model objects representing data stored in Realms. @@ -137,11 +137,13 @@ NS_ASSUME_NONNULL_BEGIN on them. The `value` argument can be a key-value coding compliant object, an array or dictionary returned from the methods in - `NSJSONSerialization`, or an array containing one element for each managed property. An exception will be thrown if - any required properties are not present and those properties were not defined with default values. + `NSJSONSerialization`, or an array containing one element for each managed property. - When passing in an array as the `value` argument, all properties must be present, valid and in the same order as the - properties defined in the model. + An exception will be thrown if any required properties are not present and those properties + were not defined with default values. + + If the `value` argument is an array, all properties must be present, valid and in the same + order as the properties defined in the model. @param value The value used to populate the object. @@ -156,11 +158,13 @@ NS_ASSUME_NONNULL_BEGIN on them. The `value` argument can be a key-value coding compliant object, an array or dictionary returned from the methods in - `NSJSONSerialization`, or an array containing one element for each managed property. An exception will be thrown if any - required properties are not present and those properties were not defined with default values. + `NSJSONSerialization`, or an array containing one element for each managed property. + + An exception will be thrown if any required properties are not present and those properties + were not defined with default values. - When passing in an array as the `value` argument, all properties must be present, valid and in the same order as the - properties defined in the model. + If the `value` argument is an array, all properties must be present, valid and in the same + order as the properties defined in the model. @param realm The Realm which should manage the newly-created object. @param value The value used to populate the object. @@ -179,16 +183,24 @@ NS_ASSUME_NONNULL_BEGIN If nested objects are included in the argument, `createOrUpdateInDefaultRealmWithValue:` will be recursively called on them if they have primary keys, `createInDefaultRealmWithValue:` if they do not. - If the argument is a Realm object already managed by the default Realm, the argument's type is the same - as the receiver, and the objects have identical values for their managed properties, this method does nothing. + The `value` argument is used to populate the object. It can be a Realm object, a key-value coding + compliant object, an array or dictionary returned from the methods in `NSJSONSerialization`, or an + array containing one element for each managed property. + + If the object is being created, an exception will be thrown if any required properties + are not present and those properties were not defined with default values. - The `value` argument is used to populate the object. It can be a key-value coding compliant object, an array or - dictionary returned from the methods in `NSJSONSerialization`, or an array containing one element for each managed - property. An exception will be thrown if any required properties are not present and those properties were not defined - with default values. + If the `value` argument is a Realm object already managed by the default Realm, the + argument's type is the same as the receiver, and the objects have identical values for + their managed properties, this method does nothing. - When passing in an array as the `value` argument, all properties must be present, valid and in the same order as the - properties defined in the model. + If the object is being updated, all properties defined in its schema will be set by copying from + `value` using key-value coding. If the `value` argument does not respond to `valueForKey:` for a + given property name (or getter name, if defined), that value will remain untouched. + Nullable properties on the object can be set to nil by using `NSNull` as the updated value. + + If the `value` argument is an array, all properties must be present, valid and in the same + order as the properties defined in the model. @param value The value used to populate the object. @@ -206,16 +218,24 @@ NS_ASSUME_NONNULL_BEGIN If nested objects are included in the argument, `createOrUpdateInRealm:withValue:` will be recursively called on them if they have primary keys, `createInRealm:withValue:` if they do not. - If the argument is a Realm object already managed by the given Realm, the argument's type is the same - as the receiver, and the objects have identical values for their managed properties, this method does nothing. + The `value` argument is used to populate the object. It can be a Realm object, a key-value coding + compliant object, an array or dictionary returned from the methods in `NSJSONSerialization`, or an + array containing one element for each managed property. + + If the object is being created, an exception will be thrown if any required properties + are not present and those properties were not defined with default values. + + If the `value` argument is a Realm object already managed by the given Realm, the + argument's type is the same as the receiver, and the objects have identical values for + their managed properties, this method does nothing. - The `value` argument is used to populate the object. It can be a key-value coding compliant object, an array or - dictionary returned from the methods in `NSJSONSerialization`, or an array containing one element for each managed - property. An exception will be thrown if any required properties are not present and those properties were not defined - with default values. + If the object is being updated, all properties defined in its schema will be set by copying from + `value` using key-value coding. If the `value` argument does not respond to `valueForKey:` for a + given property name (or getter name, if defined), that value will remain untouched. + Nullable properties on the object can be set to nil by using `NSNull` as the updated value. - When passing in an array as the `value` argument, all properties must be present, valid and in the same order as the - properties defined in the model. + If the `value` argument is an array, all properties must be present, valid and in the same + order as the properties defined in the model. @param realm The Realm which should own the object. @param value The value used to populate the object. @@ -330,7 +350,7 @@ NS_ASSUME_NONNULL_BEGIN + (RLMResults *)objectsWhere:(NSString *)predicateFormat, ...; /// :nodoc: -+ (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args; ++ (RLMResults<__kindof RLMObject *> *)objectsWhere:(NSString *)predicateFormat args:(va_list)args; /** @@ -354,7 +374,7 @@ NS_ASSUME_NONNULL_BEGIN @return An object of this object type, or `nil` if an object with the given primary key does not exist. @see `-primaryKey` */ -+ (nullable instancetype)objectForPrimaryKey:(nullable id)primaryKey; ++ (nullable instancetype)objectForPrimaryKey:(nullable id)primaryKey NS_SWIFT_NAME(object(forPrimaryKey:)); #pragma mark - Querying Specific Realms @@ -379,7 +399,7 @@ NS_ASSUME_NONNULL_BEGIN + (RLMResults *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat, ...; /// :nodoc: -+ (RLMResults *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat args:(va_list)args; ++ (RLMResults<__kindof RLMObject *> *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat args:(va_list)args; /** Returns all objects of this object type matching the given predicate from the specified Realm. @@ -403,7 +423,7 @@ NS_ASSUME_NONNULL_BEGIN @return An object of this object type, or `nil` if an object with the given primary key does not exist. @see `-primaryKey` */ -+ (nullable instancetype)objectInRealm:(RLMRealm *)realm forPrimaryKey:(nullable id)primaryKey; ++ (nullable instancetype)objectInRealm:(RLMRealm *)realm forPrimaryKey:(nullable id)primaryKey NS_SWIFT_NAME(object(in:forPrimaryKey:)); #pragma mark - Notifications @@ -449,7 +469,7 @@ typedef void (^RLMObjectChangeBlock)(BOOL deleted, Only objects which are managed by a Realm can be observed in this way. You must retain the returned token for as long as you want updates to be sent to - the block. To stop receiving updates, call `stop` on the token. + the block. To stop receiving updates, call `-invalidate` on the token. It is safe to capture a strong reference to the observed object within the callback block. There is no retain cycle due to that the callback is retained @@ -469,8 +489,8 @@ typedef void (^RLMObjectChangeBlock)(BOOL deleted, Returns YES if another Realm object instance points to the same object as the receiver in the Realm managing the receiver. - For object types with a primary, key, `isEqual:` is overridden to use this method (along with a corresponding - implementation for `hash`). + For object types with a primary, key, `isEqual:` is overridden to use the same logic as this + method (along with a corresponding implementation for `hash`). @param object The object to compare the receiver to. diff --git a/Pods/Realm/include/RLMObjectBase.h b/Pods/Realm/include/RLMObjectBase.h index 6b3271f..de40598 100644 --- a/Pods/Realm/include/RLMObjectBase.h +++ b/Pods/Realm/include/RLMObjectBase.h @@ -37,6 +37,7 @@ NS_ASSUME_NONNULL_BEGIN + (BOOL)shouldIncludeInDefaultSchema; + (nullable NSString *)_realmObjectName; ++ (nullable NSDictionary *)_realmColumnNames; @end diff --git a/Pods/Realm/include/RLMObjectBase_Dynamic.h b/Pods/Realm/include/RLMObjectBase_Dynamic.h index 08f25d5..55f64ef 100644 --- a/Pods/Realm/include/RLMObjectBase_Dynamic.h +++ b/Pods/Realm/include/RLMObjectBase_Dynamic.h @@ -24,26 +24,26 @@ NS_ASSUME_NONNULL_BEGIN /** Returns the Realm that manages the object, if one exists. - + @warning This function is useful only in specialized circumstances, for example, when building components that integrate with Realm. If you are simply building an app on Realm, it is recommended to retrieve the Realm that manages the object via `RLMObject`. @param object An `RLMObjectBase` obtained via a Swift `Object` or `RLMObject`. - + @return The Realm which manages this object. Returns `nil `for unmanaged objects. */ FOUNDATION_EXTERN RLMRealm * _Nullable RLMObjectBaseRealm(RLMObjectBase * _Nullable object); /** Returns an `RLMObjectSchema` which describes the managed properties of the object. - + @warning This function is useful only in specialized circumstances, for example, when building components that integrate with Realm. If you are simply building an app on Realm, it is recommended to retrieve `objectSchema` via `RLMObject`. @param object An `RLMObjectBase` obtained via a Swift `Object` or `RLMObject`. - + @return The object schema which lists the managed properties for the object. */ FOUNDATION_EXTERN RLMObjectSchema * _Nullable RLMObjectBaseObjectSchema(RLMObjectBase * _Nullable object); @@ -56,23 +56,23 @@ FOUNDATION_EXTERN RLMObjectSchema * _Nullable RLMObjectBaseObjectSchema(RLMObjec recommended to retrieve key values via `RLMObject`. @warning Will throw an `NSUndefinedKeyException` if `key` is not present on the object. - + @param object An `RLMObjectBase` obtained via a Swift `Object` or `RLMObject`. @param key The name of the property. - + @return The object for the property requested. */ FOUNDATION_EXTERN id _Nullable RLMObjectBaseObjectForKeyedSubscript(RLMObjectBase * _Nullable object, NSString *key); /** Sets a value for a key on the object. - + @warning This function is useful only in specialized circumstances, for example, when building components that integrate with Realm. If you are simply building an app on Realm, it is recommended to set key values via `RLMObject`. @warning Will throw an `NSUndefinedKeyException` if `key` is not present on the object. - + @param object An `RLMObjectBase` obtained via a Swift `Object` or `RLMObject`. @param key The name of the property. @param obj The object to set as the value of the key. diff --git a/Pods/Realm/include/RLMSyncPermissionOfferResponse_Private.h b/Pods/Realm/include/RLMObjectBase_Private.h similarity index 63% rename from Pods/Realm/include/RLMSyncPermissionOfferResponse_Private.h rename to Pods/Realm/include/RLMObjectBase_Private.h index 88e7809..c2d0722 100644 --- a/Pods/Realm/include/RLMSyncPermissionOfferResponse_Private.h +++ b/Pods/Realm/include/RLMObjectBase_Private.h @@ -1,6 +1,6 @@ //////////////////////////////////////////////////////////////////////////// // -// Copyright 2016 Realm Inc. +// Copyright 2017 Realm Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,21 +16,13 @@ // //////////////////////////////////////////////////////////////////////////// -#import "RLMSyncPermissionOfferResponse.h" +#import NS_ASSUME_NONNULL_BEGIN -@interface RLMSyncPermissionOfferResponse() - -@property (readwrite) NSString *id; -@property (readwrite) NSDate *createdAt; -@property (readwrite) NSDate *updatedAt; -@property (nullable, readwrite) NSNumber *statusCode; -@property (nullable, readwrite) NSString *statusMessage; - -@property (readwrite) NSString *token; -@property (nullable, readwrite) NSString *realmUrl; - +// RLMObjectBase private +@interface RLMObjectBase () ++ (void)initializeLinkedObjectSchemas; @end NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMObjectSchema.h b/Pods/Realm/include/RLMObjectSchema.h index 808cdfa..83a7d84 100644 --- a/Pods/Realm/include/RLMObjectSchema.h +++ b/Pods/Realm/include/RLMObjectSchema.h @@ -36,7 +36,7 @@ NS_ASSUME_NONNULL_BEGIN /** An array of `RLMProperty` instances representing the managed properties of a class described by the schema. - + @see `RLMProperty` */ @property (nonatomic, readonly, copy) NSArray *properties; @@ -55,9 +55,9 @@ NS_ASSUME_NONNULL_BEGIN /** Retrieves an `RLMProperty` object by the property name. - + @param propertyName The property's name. - + @return An `RLMProperty` object, or `nil` if there is no property with the given name. */ - (nullable RLMProperty *)objectForKeyedSubscript:(NSString *)propertyName; diff --git a/Pods/Realm/include/RLMObjectSchema_Private.h b/Pods/Realm/include/RLMObjectSchema_Private.h index c2968cc..deca77d 100644 --- a/Pods/Realm/include/RLMObjectSchema_Private.h +++ b/Pods/Realm/include/RLMObjectSchema_Private.h @@ -54,15 +54,15 @@ NS_ASSUME_NONNULL_BEGIN This method is useful only in specialized circumstances, for example, when accessing objects in a Realm produced externally. If you are simply building an app on Realm, it is not recommended to use this method as an [RLMObjectSchema](RLMObjectSchema) is generated automatically for every [RLMObject](RLMObject) subclass. - + Initialize an RLMObjectSchema with classname, objectClass, and an array of properties - + @warning This method is useful only in specialized circumstances. - + @param objectClassName The name of the class used to refer to objects of this type. @param objectClass The Objective-C class used when creating instances of this type. @param properties An array of RLMProperty instances describing the managed properties for this type. - + @return An initialized instance of RLMObjectSchema. */ - (instancetype)initWithClassName:(NSString *)objectClassName objectClass:(Class)objectClass properties:(NSArray *)properties; diff --git a/Pods/Realm/include/RLMObjectSchema_Private.hpp b/Pods/Realm/include/RLMObjectSchema_Private.hpp index 3d93be2..cb00dc8 100644 --- a/Pods/Realm/include/RLMObjectSchema_Private.hpp +++ b/Pods/Realm/include/RLMObjectSchema_Private.hpp @@ -18,11 +18,14 @@ #import "RLMObjectSchema_Private.h" -#import "object_schema.hpp" +namespace realm { + class ObjectSchema; +} +@class RLMSchema; @interface RLMObjectSchema () // create realm::ObjectSchema copy -- (realm::ObjectSchema)objectStoreCopy; +- (realm::ObjectSchema)objectStoreCopy:(RLMSchema *)schema; // initialize with realm::ObjectSchema + (instancetype)objectSchemaForObjectStoreSchema:(realm::ObjectSchema const&)objectSchema; diff --git a/Pods/Realm/include/RLMObjectStore.h b/Pods/Realm/include/RLMObjectStore.h index 3d7f194..b44e7e6 100644 --- a/Pods/Realm/include/RLMObjectStore.h +++ b/Pods/Realm/include/RLMObjectStore.h @@ -34,23 +34,6 @@ NS_ASSUME_NONNULL_BEGIN void RLMRealmCreateAccessors(RLMSchema *schema); -// -// Options for object creation -// -typedef NS_OPTIONS(NSUInteger, RLMCreationOptions) { - // Normal object creation - RLMCreationOptionsNone = 0, - // If the property is a link or array property, upsert the linked objects - // if they have a primary key, and insert them otherwise. - RLMCreationOptionsCreateOrUpdate = 1 << 0, - // Allow unmanaged objects to be promoted to managed objects - // if false objects are copied during object creation - RLMCreationOptionsPromoteUnmanaged = 1 << 1, - // Use the SetDefault instruction. - RLMCreationOptionsSetDefault = 1 << 2, -}; - - // // Adding, Removing, Getting Objects // @@ -72,16 +55,17 @@ NS_RETURNS_RETAINED; id _Nullable RLMGetObject(RLMRealm *realm, NSString *objectClassName, id _Nullable key) NS_RETURNS_RETAINED; // create object from array or dictionary -RLMObjectBase *RLMCreateObjectInRealmWithValue(RLMRealm *realm, NSString *className, id _Nullable value, bool createOrUpdate) +RLMObjectBase *RLMCreateObjectInRealmWithValue(RLMRealm *realm, NSString *className, + id _Nullable value, bool createOrUpdate) NS_RETURNS_RETAINED; - + // // Accessor Creation // -// switch List<> properties from being backed by unmanaged RLMArrays to RLMArrayLinkView +// switch List<> properties from being backed by unmanaged RLMArrays to RLMManagedArray void RLMInitializeSwiftAccessorGenerics(RLMObjectBase *object); #ifdef __cplusplus diff --git a/Pods/Realm/include/RLMObject_Private.h b/Pods/Realm/include/RLMObject_Private.h index cdb087f..33a13ae 100644 --- a/Pods/Realm/include/RLMObject_Private.h +++ b/Pods/Realm/include/RLMObject_Private.h @@ -20,6 +20,9 @@ NS_ASSUME_NONNULL_BEGIN +@class RLMProperty, RLMArray, RLMSwiftPropertyMetadata; +typedef NS_ENUM(int32_t, RLMPropertyType); + // RLMObject accessor and read/write realm @interface RLMObjectBase () { @public @@ -81,24 +84,64 @@ typedef void (^RLMObjectNotificationCallback)(NSArray *_Nullable pro NSError *_Nullable error); FOUNDATION_EXTERN RLMNotificationToken *RLMObjectAddNotificationBlock(RLMObjectBase *obj, RLMObjectNotificationCallback block); +// Returns whether the class is a descendent of RLMObjectBase +FOUNDATION_EXTERN BOOL RLMIsObjectOrSubclass(Class klass); + +// Returns whether the class is an indirect descendant of RLMObjectBase +FOUNDATION_EXTERN BOOL RLMIsObjectSubclass(Class klass); + +// For unit testing purposes, allow an Objective-C class named FakeObject to also be used +// as the base class of managed objects. This allows for testing invalid schemas. +FOUNDATION_EXTERN void RLMSetTreatFakeObjectAsRLMObject(BOOL flag); + // Get ObjectUil class for objc or swift FOUNDATION_EXTERN Class RLMObjectUtilClass(BOOL isSwift); FOUNDATION_EXTERN const NSUInteger RLMDescriptionMaxDepth; -@class RLMProperty, RLMArray; @interface RLMObjectUtil : NSObject + (nullable NSArray *)ignoredPropertiesForClass:(Class)cls; + (nullable NSArray *)indexedPropertiesForClass:(Class)cls; + (nullable NSDictionary *> *)linkingObjectsPropertiesForClass:(Class)cls; -+ (nullable NSArray *)getGenericListPropertyNames:(id)obj; -+ (nullable NSDictionary *)getLinkingObjectsProperties:(id)object; +// Precondition: these must be returned in ascending order. ++ (nullable NSArray *)getSwiftProperties:(id)obj; + (nullable NSDictionary *)getOptionalProperties:(id)obj; + (nullable NSArray *)requiredPropertiesForClass:(Class)cls; @end +typedef NS_ENUM(NSUInteger, RLMSwiftPropertyKind) { + RLMSwiftPropertyKindList, + RLMSwiftPropertyKindLinkingObjects, + RLMSwiftPropertyKindOptional, + RLMSwiftPropertyKindNilLiteralOptional, // For Swift optional properties that reflect as nil + RLMSwiftPropertyKindOther, +}; + +// Metadata that describes a Swift generic property. +@interface RLMSwiftPropertyMetadata : NSObject + +@property (nonatomic, strong) NSString *propertyName; +@property (nullable, nonatomic, strong) NSString *className; +@property (nullable, nonatomic, strong) NSString *linkedPropertyName; +@property (nonatomic) RLMPropertyType propertyType; +@property (nonatomic) RLMSwiftPropertyKind kind; + ++ (instancetype)metadataForOtherProperty:(NSString *)propertyName; + ++ (instancetype)metadataForListProperty:(NSString *)propertyName; + ++ (instancetype)metadataForLinkingObjectsProperty:(NSString *)propertyName + className:(NSString *)className + linkedPropertyName:(NSString *)linkedPropertyName; + ++ (instancetype)metadataForOptionalProperty:(NSString *)propertyName type:(RLMPropertyType)type; + ++ (instancetype)metadataForNilLiteralOptionalProperty:(NSString *)propertyName; + +@end + NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMOptionalBase.h b/Pods/Realm/include/RLMOptionalBase.h index 5ee261e..d5b2430 100644 --- a/Pods/Realm/include/RLMOptionalBase.h +++ b/Pods/Realm/include/RLMOptionalBase.h @@ -24,15 +24,13 @@ NS_ASSUME_NONNULL_BEGIN @class RLMObjectBase, RLMProperty; @interface RLMOptionalBase : NSProxy - - (instancetype)init; +@end -@property (nonatomic, weak) RLMObjectBase *object; - -@property (nonatomic, unsafe_unretained) RLMProperty *property; - -@property (nonatomic, strong, nullable) id underlyingValue; +FOUNDATION_EXTERN id _Nullable RLMGetOptional(RLMOptionalBase *); +FOUNDATION_EXTERN void RLMSetOptional(RLMOptionalBase *, id _Nullable); -@end +void RLMInitializeManagedOptional(RLMOptionalBase *, RLMObjectBase *parent, RLMProperty *prop); +void RLMInitializeUnmanagedOptional(RLMOptionalBase *, RLMObjectBase *parent, RLMProperty *prop); NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMPlatform.h b/Pods/Realm/include/RLMPlatform.h index e69de29..8b13789 100644 --- a/Pods/Realm/include/RLMPlatform.h +++ b/Pods/Realm/include/RLMPlatform.h @@ -0,0 +1 @@ + diff --git a/Pods/Realm/include/RLMProperty.h b/Pods/Realm/include/RLMProperty.h index f19c592..a2bc894 100644 --- a/Pods/Realm/include/RLMProperty.h +++ b/Pods/Realm/include/RLMProperty.h @@ -16,37 +16,37 @@ // //////////////////////////////////////////////////////////////////////////// -#import #import NS_ASSUME_NONNULL_BEGIN /// :nodoc: -@protocol RLMInt -@end - +@protocol RLMInt @end /// :nodoc: -@protocol RLMBool -@end - +@protocol RLMBool @end /// :nodoc: -@protocol RLMDouble -@end - +@protocol RLMDouble @end /// :nodoc: -@protocol RLMFloat -@end +@protocol RLMFloat @end +/// :nodoc: +@protocol RLMString @end +/// :nodoc: +@protocol RLMDate @end +/// :nodoc: +@protocol RLMData @end /// :nodoc: @interface NSNumber () @end /** - `RLMProperty` instances represent properties managed by a Realm in the context of an object schema. Such properties may - be persisted to a Realm file or computed from other data from the Realm. - - When using Realm, `RLMProperty` instances allow performing migrations and introspecting the database's schema. - + `RLMProperty` instances represent properties managed by a Realm in the context + of an object schema. Such properties may be persisted to a Realm file or + computed from other data from the Realm. + + When using Realm, `RLMProperty` instances allow performing migrations and + introspecting the database's schema. + These property instances map to columns in the core database. */ @interface RLMProperty : NSObject @@ -60,14 +60,14 @@ NS_ASSUME_NONNULL_BEGIN /** The type of the property. - + @see `RLMPropertyType` */ @property (nonatomic, readonly) RLMPropertyType type; /** Indicates whether this property is indexed. - + @see `RLMObject` */ @property (nonatomic, readonly) BOOL indexed; @@ -87,6 +87,11 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, readonly) BOOL optional; +/** + Indicates whether this property is an array. + */ +@property (nonatomic, readonly) BOOL array; + #pragma mark - Methods /** diff --git a/Pods/Realm/include/RLMProperty_Private.h b/Pods/Realm/include/RLMProperty_Private.h index 0439b0a..1e4e5ec 100644 --- a/Pods/Realm/include/RLMProperty_Private.h +++ b/Pods/Realm/include/RLMProperty_Private.h @@ -24,10 +24,36 @@ NS_ASSUME_NONNULL_BEGIN -BOOL RLMPropertyTypeIsNullable(RLMPropertyType propertyType); BOOL RLMPropertyTypeIsComputed(RLMPropertyType propertyType); FOUNDATION_EXTERN void RLMValidateSwiftPropertyName(NSString *name); +// Translate an rlmtype to a string representation +static inline NSString *RLMTypeToString(RLMPropertyType type) { + switch (type) { + case RLMPropertyTypeString: + return @"string"; + case RLMPropertyTypeInt: + return @"int"; + case RLMPropertyTypeBool: + return @"bool"; + case RLMPropertyTypeDate: + return @"date"; + case RLMPropertyTypeData: + return @"data"; + case RLMPropertyTypeDouble: + return @"double"; + case RLMPropertyTypeFloat: + return @"float"; + case RLMPropertyTypeAny: + return @"any"; + case RLMPropertyTypeObject: + return @"object"; + case RLMPropertyTypeLinkingObjects: + return @"linking objects"; + } + return @"Unknown"; +} + // private property interface @interface RLMProperty () { @public @@ -47,8 +73,7 @@ FOUNDATION_EXTERN void RLMValidateSwiftPropertyName(NSString *name); instance:(RLMObjectBase *)objectInstance; - (instancetype)initSwiftListPropertyWithName:(NSString *)name - ivar:(Ivar)ivar - objectClassName:(nullable NSString *)objectClassName; + instance:(id)object; - (instancetype)initSwiftOptionalPropertyWithName:(NSString *)name indexed:(BOOL)indexed @@ -68,6 +93,7 @@ FOUNDATION_EXTERN void RLMValidateSwiftPropertyName(NSString *name); @property (nonatomic, copy, nullable) NSString *objectClassName; // private properties +@property (nonatomic, readwrite) NSString *columnName; @property (nonatomic, assign) NSUInteger index; @property (nonatomic, assign) BOOL isPrimary; @property (nonatomic, assign) Ivar swiftIvar; @@ -87,11 +113,11 @@ FOUNDATION_EXTERN void RLMValidateSwiftPropertyName(NSString *name); This method is useful only in specialized circumstances, for example, in conjunction with +[RLMObjectSchema initWithClassName:objectClass:properties:]. If you are simply building an app on Realm, it is not recommened to use this method. - + Initialize an RLMProperty - + @warning This method is useful only in specialized circumstances. - + @param name The property name. @param type The property type. @param objectClassName The object type used for Object and Array types. diff --git a/Pods/Realm/include/RLMProperty_Private.hpp b/Pods/Realm/include/RLMProperty_Private.hpp index 0e214d5..cf8b17a 100644 --- a/Pods/Realm/include/RLMProperty_Private.hpp +++ b/Pods/Realm/include/RLMProperty_Private.hpp @@ -18,12 +18,16 @@ #import -#import "property.hpp" +namespace realm { + struct Property; +} + +@class RLMSchema; @interface RLMProperty () + (instancetype)propertyForObjectStoreProperty:(const realm::Property&)property; -- (realm::Property)objectStoreCopy; +- (realm::Property)objectStoreCopy:(RLMSchema *)schema; @end diff --git a/Pods/Realm/include/RLMQueryUtil.hpp b/Pods/Realm/include/RLMQueryUtil.hpp index 1be0c4e..12df76e 100644 --- a/Pods/Realm/include/RLMQueryUtil.hpp +++ b/Pods/Realm/include/RLMQueryUtil.hpp @@ -37,6 +37,3 @@ realm::Query RLMPredicateToQuery(NSPredicate *predicate, RLMObjectSchema *object // return property - throw for invalid column name RLMProperty *RLMValidatedProperty(RLMObjectSchema *objectSchema, NSString *columnName); - -// validate the array of RLMSortDescriptors and convert it to a realm::SortDescriptor -realm::SortDescriptor RLMSortDescriptorFromDescriptors(RLMClassInfo& classInfo, NSArray *descriptors); diff --git a/Pods/Realm/include/RLMRealm+Sync.h b/Pods/Realm/include/RLMRealm+Sync.h new file mode 100644 index 0000000..ccfcb02 --- /dev/null +++ b/Pods/Realm/include/RLMRealm+Sync.h @@ -0,0 +1,56 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2017 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +#import "RLMRealm.h" + +@class RLMResults, RLMSyncSession; + +/** + A callback used to vend the results of a partial sync fetch. + */ +typedef void(^RLMPartialSyncFetchCallback)(RLMResults * _Nullable results, NSError * _Nullable error); + +NS_ASSUME_NONNULL_BEGIN + +/// +@interface RLMRealm (Sync) + +/** + If the Realm is a partially synchronized Realm, fetch and synchronize the objects + of a given object type that match the given query (in string format). + + The results will be returned asynchronously in the callback. + Use `-[RLMResults addNotificationBlock:]` to be notified to changes to the set of + synchronized objects. + + @warning Partial synchronization is a tech preview. Its APIs are subject to change. +*/ +- (void)subscribeToObjects:(Class)type where:(NSString *)query callback:(RLMPartialSyncFetchCallback)callback +__deprecated_msg("Use -[RLMResults subscribe]"); + +/** + Get the RLMSyncSession used by this Realm. Will be nil if this is not a + synchronized Realm. +*/ +@property (nonatomic, nullable, readonly) RLMSyncSession *syncSession; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMRealm.h b/Pods/Realm/include/RLMRealm.h index 0f7ee4f..aa55417 100644 --- a/Pods/Realm/include/RLMRealm.h +++ b/Pods/Realm/include/RLMRealm.h @@ -19,7 +19,17 @@ #import #import "RLMConstants.h" -@class RLMRealmConfiguration, RLMObject, RLMSchema, RLMMigration, RLMNotificationToken, RLMThreadSafeReference; +@class RLMRealmConfiguration, RLMRealm, RLMObject, RLMSchema, RLMMigration, RLMNotificationToken, RLMThreadSafeReference; +struct RLMRealmPrivileges; +struct RLMClassPrivileges; +struct RLMObjectPrivileges; + +/** + A callback block for opening Realms asynchronously. + + Returns the Realm if the open was successful, or an error otherwise. + */ +typedef void(^RLMAsyncOpenRealmCallback)(RLMRealm * _Nullable realm, NSError * _Nullable error); NS_ASSUME_NONNULL_BEGIN @@ -90,6 +100,31 @@ NS_ASSUME_NONNULL_BEGIN */ + (instancetype)realmWithURL:(NSURL *)fileURL; +/** + Asynchronously open a Realm and deliver it to a block on the given queue. + + Opening a Realm asynchronously will perform all work needed to get the Realm to + a usable state (such as running potentially time-consuming migrations) on a + background thread before dispatching to the given queue. In addition, + synchronized Realms wait for all remote content available at the time the + operation began to be downloaded and available locally. + + @param configuration A configuration object to use when opening the Realm. + @param callbackQueue The dispatch queue on which the callback should be run. + @param callback A callback block. If the Realm was successfully opened, + it will be passed in as an argument. + Otherwise, an `NSError` describing what went wrong will be + passed to the block instead. + + @note The returned Realm is confined to the thread on which it was created. + Because GCD does not guarantee that queues will always use the same + thread, accessing the returned Realm outside the callback block (even if + accessed from `callbackQueue`) is unsafe. + */ ++ (void)asyncOpenWithConfiguration:(RLMRealmConfiguration *)configuration + callbackQueue:(dispatch_queue_t)callbackQueue + callback:(RLMAsyncOpenRealmCallback)callback; + /** The `RLMSchema` used by the Realm. */ @@ -458,12 +493,12 @@ NS_REFINED_FOR_SWIFT; @warning This method may only be called during a write transaction. - @param array An enumerable object such as `NSArray` or `RLMResults` which contains objects to be added to - the Realm. + @param objects An enumerable collection such as `NSArray`, `RLMArray`, or `RLMResults`, + containing Realm objects to be added to the Realm. @see `addObject:` */ -- (void)addObjects:(id)array; +- (void)addObjects:(id)objects; /** Adds or updates an existing object into the Realm. @@ -476,6 +511,10 @@ NS_REFINED_FOR_SWIFT; Realm. Use `-[RLMObject createOrUpdateInRealm:withValue:]` to copy values to a different Realm. + If there is a property or KVC value on `object` whose value is nil, and it corresponds + to a nullable property on an existing object being updated, that nullable property will + be set to nil. + @warning This method may only be called during a write transaction. @param object The object to be added or updated. @@ -489,11 +528,12 @@ NS_REFINED_FOR_SWIFT; @warning This method may only be called during a write transaction. - @param array An `NSArray`, `RLMArray`, or `RLMResults` of `RLMObject`s (or subclasses) to be added to the Realm. + @param objects An enumerable collection such as `NSArray`, `RLMArray`, or `RLMResults`, + containing Realm objects to be added to or updated within the Realm. @see `addOrUpdateObject:` */ -- (void)addOrUpdateObjectsFromArray:(id)array; +- (void)addOrUpdateObjects:(id)objects; /** Deletes an object from the Realm. Once the object is deleted it is considered invalidated. @@ -511,11 +551,12 @@ NS_REFINED_FOR_SWIFT; @warning This method may only be called during a write transaction. - @param array An `RLMArray`, `NSArray`, or `RLMResults` of `RLMObject`s (or subclasses) to be deleted. + @param objects An enumerable collection such as `NSArray`, `RLMArray`, or `RLMResults`, + containing objects to be deleted from the Realm. @see `deleteObject:` */ -- (void)deleteObjects:(id)array; +- (void)deleteObjects:(id)objects; /** Deletes all objects from the Realm. @@ -566,37 +607,130 @@ NS_REFINED_FOR_SWIFT; @see RLMMigration */ -+ (nullable NSError *)migrateRealm:(RLMRealmConfiguration *)configuration -__deprecated_msg("Use `performMigrationForConfiguration:error:`") NS_REFINED_FOR_SWIFT; ++ (BOOL)performMigrationForConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error; + +#pragma mark - Privileges /** - Performs the given Realm configuration's migration block on a Realm at the given path. + Returns the computed privileges which the current user has for this Realm. - This method is called automatically when opening a Realm for the first time and does - not need to be called explicitly. You can choose to call this method to control - exactly when and how migrations are performed. + This combines all privileges granted on the Realm by all Roles which the + current User is a member of into the final privileges which will be enforced by + the server. - @param configuration The Realm configuration used to open and migrate the Realm. - @return The error that occurred while applying the migration, if any. + The privilege calculation is done locally using cached data, and inherently may + be stale. It is possible that this method may indicate that an operation is + permitted but the server will still reject it if permission is revoked before + the changes have been integrated on the server. - @see RLMMigration + Non-synchronized Realms always have permission to perform all operations. + + @warning This currently returns incorrect results for non-partially-synchronized read-only Realms. + @return The privileges which the current user has for the current Realm. */ -+ (BOOL)performMigrationForConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error; +- (struct RLMRealmPrivileges)privilegesForRealm; + +/** + Returns the computed privileges which the current user has for the given object. + + This combines all privileges granted on the object by all Roles which the + current User is a member of into the final privileges which will be enforced by + the server. + + The privilege calculation is done locally using cached data, and inherently may + be stale. It is possible that this method may indicate that an operation is + permitted but the server will still reject it if permission is revoked before + the changes have been integrated on the server. + + Non-synchronized Realms always have permission to perform all operations. + + The object must be a valid object managed by this Realm. Passing in an + invalidated object, an unmanaged object, or an object managed by a different + Realm will throw an exception. + + @warning This currently returns incorrect results for non-partially-synchronized read-only Realms. + @return The privileges which the current user has for the given object. + */ +- (struct RLMObjectPrivileges)privilegesForObject:(RLMObject *)object; + +/** + Returns the computed privileges which the current user has for the given class. + + This combines all privileges granted on the class by all Roles which the + current User is a member of into the final privileges which will be enforced by + the server. + + The privilege calculation is done locally using cached data, and inherently may + be stale. It is possible that this method may indicate that an operation is + permitted but the server will still reject it if permission is revoked before + the changes have been integrated on the server. + + Non-synchronized Realms always have permission to perform all operations. + + @warning This currently returns incorrect results for non-partially-synchronized read-only Realms. + @return The privileges which the current user has for the given object. + */ +- (struct RLMClassPrivileges)privilegesForClass:(Class)cls; + +/** + Returns the computed privileges which the current user has for the named class. + + This combines all privileges granted on the class by all Roles which the + current User is a member of into the final privileges which will be enforced by + the server. + + The privilege calculation is done locally using cached data, and inherently may + be stale. It is possible that this method may indicate that an operation is + permitted but the server will still reject it if permission is revoked before + the changes have been integrated on the server. + + Non-synchronized Realms always have permission to perform all operations. + + @warning This currently returns incorrect results for non-partially-synchronized read-only Realms. + @return The privileges which the current user has for the given object. + */ +- (struct RLMClassPrivileges)privilegesForClassNamed:(NSString *)className; + +#pragma mark - Unavailable Methods + +/** + RLMRealm instances are cached internally by Realm and cannot be created directly. + + Use `+[RLMRealm defaultRealm]`, `+[RLMRealm realmWithConfiguration:error:]` or + `+[RLMRealm realmWithURL]` to obtain a reference to an RLMRealm. + */ +- (instancetype)init __attribute__((unavailable("Use +defaultRealm, +realmWithConfiguration: or +realmWithURL:."))); + +/** + RLMRealm instances are cached internally by Realm and cannot be created directly. + + Use `+[RLMRealm defaultRealm]`, `+[RLMRealm realmWithConfiguration:error:]` or + `+[RLMRealm realmWithURL]` to obtain a reference to an RLMRealm. + */ ++ (instancetype)new __attribute__((unavailable("Use +defaultRealm, +realmWithConfiguration: or +realmWithURL:."))); + +/// :nodoc: +- (void)addOrUpdateObjectsFromArray:(id)array __attribute__((unavailable("Renamed to -addOrUpdateObjects:."))); @end +// MARK: - RLMNotificationToken + /** A token which is returned from methods which subscribe to changes to a Realm. Change subscriptions in Realm return an `RLMNotificationToken` instance, which can be used to unsubscribe from the changes. You must store a strong reference to the token for as long as you want to continue to receive notifications. - When you wish to stop, call the `-stop` method. Notifications are also stopped if + When you wish to stop, call the `-invalidate` method. Notifications are also stopped if the token is deallocated. */ @interface RLMNotificationToken : NSObject /// Stops notifications for the change subscription that returned this token. -- (void)stop; +- (void)invalidate; + +/// Stops notifications for the change subscription that returned this token. +- (void)stop __attribute__((unavailable("Renamed to -invalidate."))) NS_REFINED_FOR_SWIFT; @end NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMRealmConfiguration+Sync.h b/Pods/Realm/include/RLMRealmConfiguration+Sync.h index 2a7aca5..abd51bf 100644 --- a/Pods/Realm/include/RLMRealmConfiguration+Sync.h +++ b/Pods/Realm/include/RLMRealmConfiguration+Sync.h @@ -22,17 +22,18 @@ @class RLMSyncConfiguration; -/// :nodoc: +/// Realm configuration options related to Sync. @interface RLMRealmConfiguration (Sync) NS_ASSUME_NONNULL_BEGIN /** - A configuration object representing configuration state for Realms intended to sync with a Realm Object Server. - - This property is mutually exclusive with both `inMemoryIdentifier` and `fileURL`; setting one will nil out the other - two. - + A configuration object representing configuration state for Realms intended + to sync with a Realm Object Server. + + This property is mutually exclusive with both `inMemoryIdentifier` and `fileURL`; + setting any one of the three properties will automatically nil out the other two. + @see `RLMSyncConfiguration` */ @property (nullable, nonatomic) RLMSyncConfiguration *syncConfiguration; diff --git a/Pods/Realm/include/RLMRealmConfiguration.h b/Pods/Realm/include/RLMRealmConfiguration.h index 21e163b..3ec8fd5 100644 --- a/Pods/Realm/include/RLMRealmConfiguration.h +++ b/Pods/Realm/include/RLMRealmConfiguration.h @@ -21,6 +21,17 @@ NS_ASSUME_NONNULL_BEGIN +/** + A block called when opening a Realm for the first time during the life + of a process to determine if it should be compacted before being returned + to the user. It is passed the total file size (data + free space) and the total + bytes used by data in the file. + + Return `YES` to indicate that an attempt to compact the file should be made. + The compaction will be skipped if another process is accessing it. + */ +typedef BOOL (^RLMShouldCompactOnLaunchBlock)(NSUInteger totalBytes, NSUInteger bytesUsed); + /** An `RLMRealmConfiguration` instance describes the different options used to create an instance of a Realm. @@ -28,10 +39,10 @@ NS_ASSUME_NONNULL_BEGIN `RLMRealmConfiguration` instances are just plain `NSObject`s. Unlike `RLMRealm`s and `RLMObject`s, they can be freely shared between threads as long as you do not mutate them. - + Creating configuration objects for class subsets (by setting the `objectClasses` property) can be expensive. Because of this, you will normally want to - cache and reuse a single configuration object for each distinct configuration rather than + cache and reuse a single configuration object for each distinct configuration rather than creating a new object each time you open a Realm. */ @interface RLMRealmConfiguration : NSObject @@ -55,10 +66,12 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Properties -/// The local URL of the Realm file. Mutually exclusive with `inMemoryIdentifier`. +/// The local URL of the Realm file. Mutually exclusive with `inMemoryIdentifier` and `syncConfiguration`; +/// setting any one of the three properties will automatically nil out the other two. @property (nonatomic, copy, nullable) NSURL *fileURL; -/// A string used to identify a particular in-memory Realm. Mutually exclusive with `fileURL`. +/// A string used to identify a particular in-memory Realm. Mutually exclusive with `fileURL` and `syncConfiguration`; +/// setting any one of the three properties will automatically nil out the other two. @property (nonatomic, copy, nullable) NSString *inMemoryIdentifier; /// A 64-byte key to use to encrypt the data, or `nil` if encryption is not enabled. @@ -91,6 +104,17 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic) BOOL deleteRealmIfMigrationNeeded; +/** + A block called when opening a Realm for the first time during the life + of a process to determine if it should be compacted before being returned + to the user. It is passed the total file size (data + free space) and the total + bytes used by data in the file. + + Return `YES` to indicate that an attempt to compact the file should be made. + The compaction will be skipped if another process is accessing it. + */ +@property (nonatomic, copy, nullable) RLMShouldCompactOnLaunchBlock shouldCompactOnLaunch; + /// The classes managed by the Realm. @property (nonatomic, copy, nullable) NSArray *objectClasses; diff --git a/Pods/Realm/include/RLMRealmConfiguration_Private.h b/Pods/Realm/include/RLMRealmConfiguration_Private.h index 7581af1..b3e4784 100644 --- a/Pods/Realm/include/RLMRealmConfiguration_Private.h +++ b/Pods/Realm/include/RLMRealmConfiguration_Private.h @@ -28,11 +28,14 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readwrite) bool dynamic; @property (nonatomic, readwrite) bool disableFormatUpgrade; @property (nonatomic, copy, nullable) RLMSchema *customSchema; +@property (nonatomic, copy) NSString *pathOnDisk; // Get the default confiugration without copying it + (RLMRealmConfiguration *)rawDefaultConfiguration; + (void)resetRealmConfigurationState; + +- (void)setCustomSchemaWithoutCopying:(nullable RLMSchema *)schema; @end // Get a path in the platform-appropriate documents directory with the given filename diff --git a/Pods/Realm/include/RLMRealmUtil.hpp b/Pods/Realm/include/RLMRealmUtil.hpp index 36dbc84..4960b9c 100644 --- a/Pods/Realm/include/RLMRealmUtil.hpp +++ b/Pods/Realm/include/RLMRealmUtil.hpp @@ -16,7 +16,6 @@ // //////////////////////////////////////////////////////////////////////////// -#import #import #import @@ -34,5 +33,7 @@ RLMRealm *RLMGetThreadLocalCachedRealmForPath(std::string const& path); RLMRealm *RLMGetAnyCachedRealmForPath(std::string const& path); // Clear the weak cache of Realms void RLMClearRealmCache(); +// Check if the current thread is currently within a running CFRunLoop +bool RLMIsInRunLoop(); std::unique_ptr RLMCreateBindingContext(RLMRealm *realm); diff --git a/Pods/Realm/include/RLMRealm_Dynamic.h b/Pods/Realm/include/RLMRealm_Dynamic.h index 3a26f49..f796ed3 100644 --- a/Pods/Realm/include/RLMRealm_Dynamic.h +++ b/Pods/Realm/include/RLMRealm_Dynamic.h @@ -21,7 +21,7 @@ #import #import -@class RLMResults; +@class RLMResults; NS_ASSUME_NONNULL_BEGIN @@ -42,7 +42,7 @@ NS_ASSUME_NONNULL_BEGIN @see `+[RLMObject allObjects]` */ -- (RLMResults *)allObjects:(NSString *)className; +- (RLMResults *)allObjects:(NSString *)className; /** Returns all objects matching the given predicate from the Realm. @@ -58,7 +58,7 @@ NS_ASSUME_NONNULL_BEGIN @see `+[RLMObject objectsWhere:]` */ -- (RLMResults *)objects:(NSString *)className where:(NSString *)predicateFormat, ...; +- (RLMResults *)objects:(NSString *)className where:(NSString *)predicateFormat, ...; /** Returns all objects matching the given predicate from the Realm. @@ -74,27 +74,27 @@ NS_ASSUME_NONNULL_BEGIN @see `+[RLMObject objectsWhere:]` */ -- (RLMResults *)objects:(NSString *)className withPredicate:(NSPredicate *)predicate; +- (RLMResults *)objects:(NSString *)className withPredicate:(NSPredicate *)predicate; /** Returns the object of the given type with the given primary key from the Realm. - @warning This method is useful only in specialized circumstances, for example, when building components + @warning This method is useful only in specialized circumstances, for example, when building components that integrate with Realm. The preferred way to get an object of a single class is to use the class methods on `RLMObject`. - + @param className The class name for the object you are looking for. @param primaryKey The primary key value for the object you are looking for. - + @return An object, or `nil` if an object with the given primary key does not exist. - + @see `+[RLMObject objectForPrimaryKey:]` */ - (nullable RLMObject *)objectWithClassName:(NSString *)className forPrimaryKey:(id)primaryKey; /** Creates an `RLMObject` instance of type `className` in the Realm, and populates it using a given object. - + The `value` argument is used to populate the object. It can be a key-value coding compliant object, an array or dictionary returned from the methods in `NSJSONSerialization`, or an array containing one element for each managed property. An exception will be thrown if any required properties are not present and those properties were not defined diff --git a/Pods/Realm/include/RLMRealm_Private.h b/Pods/Realm/include/RLMRealm_Private.h index c8ca2d2..59b7c65 100644 --- a/Pods/Realm/include/RLMRealm_Private.h +++ b/Pods/Realm/include/RLMRealm_Private.h @@ -23,11 +23,11 @@ NS_ASSUME_NONNULL_BEGIN // Disable syncing files to disk. Cannot be re-enabled. Use only for tests. -FOUNDATION_EXTERN void RLMDisableSyncToDisk(); +FOUNDATION_EXTERN void RLMDisableSyncToDisk(void); FOUNDATION_EXTERN NSData * _Nullable RLMRealmValidatedEncryptionKey(NSData *key); -// Translate an in-flight exception resulting from opening a SharedGroup to +// Translate an in-flight exception resulting from an operation on a SharedGroup to // an NSError or NSException (if error is nil) void RLMRealmTranslateException(NSError **error); @@ -45,7 +45,7 @@ void RLMRealmTranslateException(NSError **error); - (void)sendNotifications:(RLMNotification)notification; - (void)verifyThread; -- (void)verifyNotificationsAreSupported; +- (void)verifyNotificationsAreSupported:(bool)isCollection; @end diff --git a/Pods/Realm/include/RLMResults.h b/Pods/Realm/include/RLMResults.h index 24fda51..d8d1cf6 100644 --- a/Pods/Realm/include/RLMResults.h +++ b/Pods/Realm/include/RLMResults.h @@ -16,12 +16,11 @@ // //////////////////////////////////////////////////////////////////////////// -#import #import NS_ASSUME_NONNULL_BEGIN -@class RLMObject, RLMRealm, RLMNotificationToken; +@class RLMObject; /** `RLMResults` is an auto-updating container type in Realm returned from object @@ -38,8 +37,8 @@ NS_ASSUME_NONNULL_BEGIN enumeration. `RLMResults` are lazily evaluated the first time they are accessed; they only - run queries when the result of the query is requested. This means that - chaining several temporary `RLMResults` to sort and filter your data does not + run queries when the result of the query is requested. This means that + chaining several temporary `RLMResults` to sort and filter your data does not perform any extra work processing the intermediate state. Once the results have been evaluated or a notification block has been added, @@ -48,7 +47,7 @@ NS_ASSUME_NONNULL_BEGIN `RLMResults` cannot be directly instantiated. */ -@interface RLMResults : NSObject +@interface RLMResults : NSObject #pragma mark - Properties @@ -58,9 +57,21 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly, assign) NSUInteger count; /** - The class name (i.e. type) of the `RLMObject`s contained in the results collection. + The type of the objects in the results collection. */ -@property (nonatomic, readonly, copy) NSString *objectClassName; +@property (nonatomic, readonly, assign) RLMPropertyType type; + +/** + Indicates whether the objects in the collection can be `nil`. + */ +@property (nonatomic, readwrite, getter = isOptional) BOOL optional; + +/** + The class name of the objects contained in the results collection. + + Will be `nil` if `type` is not RLMPropertyTypeObject. + */ +@property (nonatomic, readonly, copy, nullable) NSString *objectClassName; /** The Realm which manages this results collection. @@ -82,7 +93,7 @@ NS_ASSUME_NONNULL_BEGIN @param index The index to look up. - @return An `RLMObject` of the type contained in the results collection. + @return An object of the type contained in the results collection. */ - (RLMObjectType)objectAtIndex:(NSUInteger)index; @@ -91,7 +102,7 @@ NS_ASSUME_NONNULL_BEGIN Returns `nil` if called on an empty results collection. - @return An `RLMObject` of the type contained in the results collection. + @return An object of the type contained in the results collection. */ - (nullable RLMObjectType)firstObject; @@ -100,7 +111,7 @@ NS_ASSUME_NONNULL_BEGIN Returns `nil` if called on an empty results collection. - @return An `RLMObject` of the type contained in the results collection. + @return An object of the type contained in the results collection. */ - (nullable RLMObjectType)lastObject; @@ -167,17 +178,6 @@ NS_ASSUME_NONNULL_BEGIN */ - (RLMResults *)sortedResultsUsingKeyPath:(NSString *)keyPath ascending:(BOOL)ascending; -/** - Returns a sorted `RLMResults` from an existing results collection. - - @param property The property name to sort by. - @param ascending The direction to sort in. - - @return An `RLMResults` sorted by the specified property. - */ -- (RLMResults *)sortedResultsUsingProperty:(NSString *)property ascending:(BOOL)ascending - __deprecated_msg("Use `-sortedResultsUsingKeyPath:ascending:`"); - /** Returns a sorted `RLMResults` from an existing results collection. @@ -187,6 +187,15 @@ NS_ASSUME_NONNULL_BEGIN */ - (RLMResults *)sortedResultsUsingDescriptors:(NSArray *)properties; +/** + Returns a distinct `RLMResults` from an existing results collection. + + @param keyPaths The key paths used produce distinct results + + @return An `RLMResults` made distinct based on the specified key paths + */ +- (RLMResults *)distinctResultsUsingKeyPaths:(NSArray *)keyPaths; + #pragma mark - Notifications /** @@ -201,7 +210,7 @@ NS_ASSUME_NONNULL_BEGIN which rows in the results collection were added, removed or modified. If a write transaction did not modify any objects in the results collection, the block is not called at all. See the `RLMCollectionChange` documentation for - information on how the changes are reported and an example of updating a + information on how the changes are reported and an example of updating a `UITableView`. If an error occurs the block will be called with `nil` for the results @@ -239,7 +248,7 @@ NS_ASSUME_NONNULL_BEGIN // end of run loop execution context You must retain the returned token for as long as you want updates to continue - to be sent to the block. To stop receiving updates, call `-stop` on the token. + to be sent to the block. To stop receiving updates, call `-invalidate` on the token. @warning This method cannot be called during a write transaction, or when the containing Realm is read-only. @@ -264,7 +273,7 @@ NS_ASSUME_NONNULL_BEGIN @param property The property whose minimum value is desired. Only properties of types `int`, `float`, `double`, and `NSDate` are supported. - @return The minimum value of the property. + @return The minimum value of the property, or `nil` if the Results are empty. */ - (nullable id)minOfProperty:(NSString *)property; @@ -275,10 +284,10 @@ NS_ASSUME_NONNULL_BEGIN @warning You cannot use this method on `RLMObject`, `RLMArray`, and `NSData` properties. - @param property The property whose maximum value is desired. Only properties of types `int`, `float`, `double`, and - `NSDate` are supported. + @param property The property whose maximum value is desired. Only properties of + types `int`, `float`, `double`, and `NSDate` are supported. - @return The maximum value of the property. + @return The maximum value of the property, or `nil` if the Results are empty. */ - (nullable id)maxOfProperty:(NSString *)property; @@ -289,8 +298,8 @@ NS_ASSUME_NONNULL_BEGIN @warning You cannot use this method on `RLMObject`, `RLMArray`, and `NSData` properties. - @param property The property whose values should be summed. Only properties of types `int`, `float`, and `double` are - supported. + @param property The property whose values should be summed. Only properties of + types `int`, `float`, and `double` are supported. @return The sum of the given property. */ @@ -303,11 +312,10 @@ NS_ASSUME_NONNULL_BEGIN @warning You cannot use this method on `RLMObject`, `RLMArray`, and `NSData` properties. - @param property The property whose average value should be calculated. Only properties of types `int`, `float`, and - `double` are supported. + @param property The property whose average value should be calculated. Only + properties of types `int`, `float`, and `double` are supported. - @return The average value of the given property. This will be of type `double` for both `float` and `double` - properties. + @return The average value of the given property, or `nil` if the Results are empty. */ - (nullable NSNumber *)averageOfProperty:(NSString *)property; @@ -333,7 +341,7 @@ NS_ASSUME_NONNULL_BEGIN /** `RLMLinkingObjects` is an auto-updating container type. It represents a collection of objects that link to its parent object. - + For more information, please see the "Inverse Relationships" section in the [documentation](https://realm.io/docs/objc/latest/#relationships). */ diff --git a/Pods/Realm/include/RLMResults_Private.hpp b/Pods/Realm/include/RLMResults_Private.hpp new file mode 100644 index 0000000..a4f5abf --- /dev/null +++ b/Pods/Realm/include/RLMResults_Private.hpp @@ -0,0 +1,63 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2017 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMResults_Private.h" + +#import "results.hpp" + +class RLMClassInfo; + +NS_ASSUME_NONNULL_BEGIN + +@interface RLMResults () { +@protected + realm::Results _results; +} + +/** + Initialize a 'raw' `RLMResults` using only an object store level Results. + This is only meant for applications where a results collection is being backed + by an object store object class that has no binding-level equivalent. The + consumer is responsible for bridging between the underlying objects and whatever + binding-level class is being vended out. + */ +- (instancetype)initWithResults:(realm::Results)results; + ++ (instancetype)resultsWithObjectInfo:(RLMClassInfo&)info results:(realm::Results)results; +@end + +NS_ASSUME_NONNULL_END + +// Utility functions + +[[gnu::noinline]] +[[noreturn]] +void RLMThrowResultsError(NSString * _Nullable aggregateMethod); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnullability-completeness" +template +static auto translateRLMResultsErrors(Function&& f, NSString *aggregateMethod=nil) { + try { + return f(); + } + catch (...) { + RLMThrowResultsError(aggregateMethod); + } +} +#pragma clang diagnostic pop diff --git a/Pods/Realm/include/RLMSchema.h b/Pods/Realm/include/RLMSchema.h index 2b11792..30325e4 100644 --- a/Pods/Realm/include/RLMSchema.h +++ b/Pods/Realm/include/RLMSchema.h @@ -36,7 +36,7 @@ NS_ASSUME_NONNULL_BEGIN /** An `NSArray` containing `RLMObjectSchema`s for all object types in the Realm. - + This property is intended to be used during migrations for dynamic introspection. @see `RLMObjectSchema` @@ -57,7 +57,7 @@ NS_ASSUME_NONNULL_BEGIN /** Looks up and returns an `RLMObjectSchema` for the given class name in the Realm. - + If there is no object of type `className` in the schema, an exception will be thrown. @param className The object class name. diff --git a/Pods/Realm/include/RLMSchema_Private.h b/Pods/Realm/include/RLMSchema_Private.h index f5823d1..7ef4917 100644 --- a/Pods/Realm/include/RLMSchema_Private.h +++ b/Pods/Realm/include/RLMSchema_Private.h @@ -44,6 +44,10 @@ NS_ASSUME_NONNULL_BEGIN // schema based upon all currently registered object classes + (instancetype)partialSharedSchema; +// private schema based upon all currently registered object classes. +// includes classes that are excluded from the default schema. ++ (instancetype)partialPrivateSharedSchema; + // class for string + (nullable Class)classForString:(NSString *)className; diff --git a/Pods/Realm/include/RLMSyncConfiguration.h b/Pods/Realm/include/RLMSyncConfiguration.h index 6181d91..a130cb5 100644 --- a/Pods/Realm/include/RLMSyncConfiguration.h +++ b/Pods/Realm/include/RLMSyncConfiguration.h @@ -18,6 +18,7 @@ #import +@class RLMRealmConfiguration; @class RLMSyncUser; NS_ASSUME_NONNULL_BEGIN @@ -33,11 +34,49 @@ NS_ASSUME_NONNULL_BEGIN /** The URL of the remote Realm upon the Realm Object Server. - + @warning The URL cannot end with `.realm`, `.realm.lock` or `.realm.management`. */ @property (nonatomic, readonly) NSURL *realmURL; + +/** + Whether SSL certificate validation is enabled for the connection associated + with this configuration value. SSL certificate validation is ON by default. + + @warning NEVER disable certificate validation for clients and servers in production. + */ +@property (nonatomic) BOOL enableSSLValidation; + +/** + Whether this Realm should be opened in 'partial synchronization' mode. + Partial synchronization mode means that no objects are synchronized from the remote Realm + except those matching queries that the user explicitly specifies. + + @warning Partial synchronization is a tech preview. Its APIs are subject to change. + */ +@property (nonatomic) BOOL isPartial DEPRECATED_MSG_ATTRIBUTE("Use 'fullSynchronization' instead."); + +/** + Whether this Realm should be a fully synchronized Realm. + + Synchronized Realms comes in two flavors: Query-based and Fully synchronized. + A fully synchronized Realm will automatically synchronize the entire Realm in + the background while a query-based Realm will only synchronize the data being + subscribed to. Synchronized realms are by default query-based unless this + boolean is set. + */ +@property (nonatomic) BOOL fullSynchronization; + +/** + The prefix that is prepended to the path in the HTTP request that initiates a + sync connection. The value specified must match with the server's expectation. + Changing the value of `urlPrefix` should be matched with a corresponding + change of the server's configuration. + If no value is specified here then the default `/realm-sync` path is used. +*/ +@property (nonatomic, nullable, copy) NSString *urlPrefix; + /** Create a sync configuration instance. @@ -47,7 +86,21 @@ NS_ASSUME_NONNULL_BEGIN contain the wildcard marker `~`, which will automatically be filled in with the user identity by the Realm Object Server. */ -- (instancetype)initWithUser:(RLMSyncUser *)user realmURL:(NSURL *)url; +- (instancetype)initWithUser:(RLMSyncUser *)user realmURL:(NSURL *)url __attribute__((deprecated("Use [RLMSyncUser configurationWithURL] instead"))); + +/** +Return a Realm configuration for syncing with the default Realm of the currently logged-in sync user. + +Partial synchronization is enabled in the returned configuration. + */ ++ (RLMRealmConfiguration *)automaticConfiguration __attribute__((deprecated("Use [RLMSyncUser configuration] instead"))); + +/** + Return a Realm configuration for syncing with the default Realm of the given sync user. + + Partial synchronization is enabled in the returned configuration. + */ ++ (RLMRealmConfiguration *)automaticConfigurationForUser:(RLMSyncUser *)user __attribute__((deprecated("Use [RLMSyncUser configuration] instead"))); /// :nodoc: - (instancetype)init __attribute__((unavailable("This type cannot be created directly"))); diff --git a/Pods/Realm/include/RLMSyncConfiguration_Private.h b/Pods/Realm/include/RLMSyncConfiguration_Private.h index 1001ae8..c9e0bdf 100644 --- a/Pods/Realm/include/RLMSyncConfiguration_Private.h +++ b/Pods/Realm/include/RLMSyncConfiguration_Private.h @@ -28,6 +28,13 @@ typedef NS_ENUM(NSUInteger, RLMSyncStopPolicy) { @interface RLMSyncConfiguration () +- (instancetype)initWithUser:(RLMSyncUser *)user + realmURL:(NSURL *)url + isPartial:(BOOL)isPartial + urlPrefix:(nullable NSString *)urlPrefix + stopPolicy:(RLMSyncStopPolicy)stopPolicy + enableSSLValidation:(BOOL)enableSSLValidation; + @property (nonatomic, readwrite) RLMSyncStopPolicy stopPolicy; // Internal-only APIs diff --git a/Pods/Realm/include/RLMSyncConfiguration_Private.hpp b/Pods/Realm/include/RLMSyncConfiguration_Private.hpp index 1a80e7f..e80095e 100644 --- a/Pods/Realm/include/RLMSyncConfiguration_Private.hpp +++ b/Pods/Realm/include/RLMSyncConfiguration_Private.hpp @@ -18,14 +18,31 @@ #import "RLMSyncConfiguration_Private.h" +#import +#import + namespace realm { +class SyncSession; struct SyncConfig; +struct SyncError; +using SyncSessionErrorHandler = void(std::shared_ptr, SyncError); } +NS_ASSUME_NONNULL_BEGIN + @interface RLMSyncConfiguration () +- (instancetype)initWithUser:(RLMSyncUser *)user + realmURL:(NSURL *)url + customFileURL:(nullable NSURL *)customFileURL + isPartial:(BOOL)isPartial + stopPolicy:(RLMSyncStopPolicy)stopPolicy + errorHandler:(std::function)errorHandler; + - (instancetype)initWithRawConfig:(realm::SyncConfig)config; - (realm::SyncConfig)rawConfiguration; @end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMSyncCredentials.h b/Pods/Realm/include/RLMSyncCredentials.h index bb19fbe..c90fb4c 100644 --- a/Pods/Realm/include/RLMSyncCredentials.h +++ b/Pods/Realm/include/RLMSyncCredentials.h @@ -45,6 +45,15 @@ extern RLMIdentityProvider const RLMIdentityProviderGoogle; /// A CloudKit account as an identity provider. extern RLMIdentityProvider const RLMIdentityProviderCloudKit; +/// A JSON Web Token as an identity provider. +extern RLMIdentityProvider const RLMIdentityProviderJWT; + +/// An Anonymous account as an identity provider. +extern RLMIdentityProvider const RLMIdentityProviderAnonymous; + +/// A Nickname account as an identity provider. +extern RLMIdentityProvider const RLMIdentityProviderNickname; + /** Opaque credentials representing a specific Realm Object Server user. */ @@ -82,8 +91,33 @@ extern RLMIdentityProvider const RLMIdentityProviderCloudKit; register:(BOOL)shouldRegister; /** - Construct and return special credentials representing a token that can be directly used to open a Realm. The identity - is used to uniquely identify the user across application launches. + Construct and return credentials from a JSON Web Token. + */ ++ (instancetype)credentialsWithJWT:(NSString *)token; + +/** + Construct and return anonymous credentials + */ ++ (instancetype)anonymousCredentials; + +/** + Construct and return credentials from a nickname + */ ++ (instancetype)credentialsWithNickname:(NSString *)nickname isAdmin:(BOOL)isAdmin; + +/** + Construct and return special credentials representing a token that can + be directly used to open a Realm. The identity is used to uniquely identify + the user across application launches. + + @warning The custom user identity will be deprecated in a future release. + + @warning Do not specify a user identity that is the URL of an authentication + server. + + @warning When passing an access token credential into any of `RLMSyncUser`'s + login methods, you must always specify the same authentication server + URL, or none at all, every time you call the login method. */ + (instancetype)credentialsWithAccessToken:(RLMServerToken)accessToken identity:(NSString *)identity; diff --git a/Pods/Realm/include/RLMSyncManager.h b/Pods/Realm/include/RLMSyncManager.h index e2f559f..af55c44 100644 --- a/Pods/Realm/include/RLMSyncManager.h +++ b/Pods/Realm/include/RLMSyncManager.h @@ -62,9 +62,16 @@ typedef void(^RLMSyncErrorReportingBlock)(NSError *, RLMSyncSession * _Nullable) @interface RLMSyncManager : NSObject /** - An optional block which can be used to report sync-related errors to your application. Errors reported through this - mechanism are always fatal; they represent attempts to open sessions which are invalid (for example, using malformed - URLs). + A block which can optionally be set to report sync-related errors to your application. + + Any error reported through this block will be of the `RLMSyncError` type, and marked + with the `RLMSyncErrorDomain` domain. + + Errors reported through this mechanism are fatal, with several exceptions. Please consult + `RLMSyncError` for information about the types of errors that can be reported through + the block, and for for suggestions on handling recoverable error codes. + + @see `RLMSyncError` */ @property (nullable, nonatomic, copy) RLMSyncErrorReportingBlock errorHandler; @@ -75,18 +82,26 @@ typedef void(^RLMSyncErrorReportingBlock)(NSError *, RLMSyncSession * _Nullable) @property (nonatomic, copy) NSString *appID; /** - Whether SSL certificate validation should be disabled. SSL certificate validation is ON by default. Setting this - property after at least one synced Realm or standalone Session has been opened is a no-op. + The logging threshold which newly opened synced Realms will use. Defaults to + `RLMSyncLogLevelInfo`. - @warning NEVER disable certificate validation for clients and servers in production. + Logging strings are output to Apple System Logger. + + @warning This property must be set before any synced Realms are opened. Setting it after + opening any synced Realm will do nothing. */ -@property (nonatomic) BOOL disableSSLValidation; +@property (nonatomic) RLMSyncLogLevel logLevel; /** - The logging threshold which newly opened synced Realms will use. Defaults to `RLMSyncLogLevelInfo`. Set this before - any synced Realms are opened. Logging strings are output to ASL. + The name of the HTTP header to send authorization data in when making requests to a Realm Object Server which has + been configured to expect a custom authorization header. */ -@property (nonatomic) RLMSyncLogLevel logLevel; +@property (nullable, nonatomic, copy) NSString *authorizationHeaderName; + +/** + Extra HTTP headers to append to every request to a Realm Object Server. + */ +@property (nullable, nonatomic, copy) NSDictionary *customRequestHeaders; /// The sole instance of the singleton. + (instancetype)sharedManager NS_REFINED_FOR_SWIFT; diff --git a/Pods/Realm/include/RLMSyncManager_Private.h b/Pods/Realm/include/RLMSyncManager_Private.h index 89fb2af..7cb660b 100644 --- a/Pods/Realm/include/RLMSyncManager_Private.h +++ b/Pods/Realm/include/RLMSyncManager_Private.h @@ -19,17 +19,7 @@ #import #import "RLMSyncUtil_Private.h" - -typedef NS_ENUM(NSUInteger, RLMSyncSystemErrorKind) { - // Specific - RLMSyncSystemErrorKindClientReset, - // General - RLMSyncSystemErrorKindClient, - RLMSyncSystemErrorKindConnection, - RLMSyncSystemErrorKindSession, - RLMSyncSystemErrorKindUser, - RLMSyncSystemErrorKindUnknown, -}; +#import "RLMNetworkClient.h" @class RLMSyncUser, RLMSyncConfiguration; @@ -55,6 +45,8 @@ NS_ASSUME_NONNULL_BEGIN + (void)resetForTesting; +- (RLMNetworkRequestOptions *)networkRequestOptions; + NS_ASSUME_NONNULL_END @end diff --git a/Pods/Realm/include/RLMSyncPermission.h b/Pods/Realm/include/RLMSyncPermission.h new file mode 100644 index 0000000..c0677a0 --- /dev/null +++ b/Pods/Realm/include/RLMSyncPermission.h @@ -0,0 +1,501 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2017 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +@protocol RLMPermission, RLMPermissionUser; +@class RLMPermission, RLMPermissionUser, RLMPermissionRole, + RLMArray, RLMLinkingObjects; + +NS_ASSUME_NONNULL_BEGIN + +/** + A permission which can be applied to a Realm, Class, or specific Object. + + Permissions are applied by adding the permission to the RLMRealmPermission singleton + object, the RLMClassPermission object for the desired class, or to a user-defined + RLMArray property on a specific Object instance. The meaning of each of + the properties of RLMPermission depend on what the permission is applied to, and so are + left undocumented here. See `RLMRealmPrivileges`, `RLMClassPrivileges`, and + `RLMObjectPrivileges` for details about what each of the properties mean when applied to + that type. + */ +@interface RLMPermission : RLMObject +/// The Role which this Permission applies to. All users within the Role are +/// granted the permissions specified by the fields below any +/// objects/classes/realms which use this Permission. +/// +/// This property cannot be modified once set. +@property (nonatomic) RLMPermissionRole *role; + +/// Whether the user can read the object to which this Permission is attached. +@property (nonatomic) bool canRead; +/// Whether the user can modify the object to which this Permission is attached. +@property (nonatomic) bool canUpdate; +/// Whether the user can delete the object to which this Permission is attached. +/// +/// This field is only applicable to Permissions attached to Objects, and not +/// to Realms or Classes. +@property (nonatomic) bool canDelete; +/// Whether the user can add or modify Permissions for the object which this +/// Permission is attached to. +@property (nonatomic) bool canSetPermissions; +/// Whether the user can subscribe to queries for this object type. +/// +/// This field is only applicable to Permissions attached to Classes, and not +/// to Realms or Objects. +@property (nonatomic) bool canQuery; +/// Whether the user can create new objects of the type this Permission is attached to. +/// +/// This field is only applicable to Permissions attached to Classes, and not +/// to Realms or Objects. +@property (nonatomic) bool canCreate; +/// Whether the user can modify the schema of the Realm which this +/// Permission is attached to. +/// +/// This field is only applicable to Permissions attached to Realms, and not +/// to Realms or Objects. +@property (nonatomic) bool canModifySchema; + +/** + Returns the Permission object for the named Role in the array, creating it if needed. + + This function should be used in preference to manually querying the array for + the applicable Permission as it ensures that there is exactly one Permission + for the given Role in the array, merging duplicates or creating and adding new + ones as needed. +*/ ++ (RLMPermission *)permissionForRoleNamed:(NSString *)roleName inArray:(RLMArray *)array; + +/** + Returns the Permission object for the named Role on the Realm, creating it if needed. + + This function should be used in preference to manually querying for the + applicable Permission as it ensures that there is exactly one Permission for + the given Role on the Realm, merging duplicates or creating and adding new ones + as needed. +*/ ++ (RLMPermission *)permissionForRoleNamed:(NSString *)roleName onRealm:(RLMRealm *)realm; + +/** + Returns the Permission object for the named Role on the Class, creating it if needed. + + This function should be used in preference to manually querying for the + applicable Permission as it ensures that there is exactly one Permission for + the given Role on the Class, merging duplicates or creating and adding new ones + as needed. +*/ ++ (RLMPermission *)permissionForRoleNamed:(NSString *)roleName onClass:(Class)cls realm:(RLMRealm *)realm; + +/** + Returns the Permission object for the named Role on the named class, creating it if needed. + + This function should be used in preference to manually querying for the + applicable Permission as it ensures that there is exactly one Permission for + the given Role on the Class, merging duplicates or creating and adding new ones + as needed. +*/ ++ (RLMPermission *)permissionForRoleNamed:(NSString *)roleName onClassNamed:(NSString *)className realm:(RLMRealm *)realm; + +/** + Returns the Permission object for the named Role on the object, creating it if needed. + + This function should be used in preference to manually querying for the + applicable Permission as it ensures that there is exactly one Permission for + the given Role on the Realm, merging duplicates or creating and adding new ones + as needed. + + The given object must have a RLMArray property defined on it. If + more than one such property is present, the first will be used. +*/ ++ (RLMPermission *)permissionForRoleNamed:(NSString *)roleName onObject:(RLMObject *)object; +@end + +/** + A Role within the permissions system. + + A Role consists of a name for the role and a list of users which are members of the role. + Roles are granted privileges on Realms, Classes and Objects, and in turn grant those + privileges to all users which are members of the role. + + A role named "everyone" is automatically created in new Realms, and all new users which + connect to the Realm are automatically added to it. Any other roles you wish to use are + managed as normal Realm objects. + */ +@interface RLMPermissionRole : RLMObject +/// The name of the Role +@property (nonatomic) NSString *name; +/// The users which belong to the role +@property (nonatomic) RLMArray *users; +@end + +/** + A representation of a sync user within the permissions system. + + RLMPermissionUser objects are created automatically for each sync user which connects to + a Realm, and can also be created manually if you wish to grant permissions to a user + which has not yet connected to this Realm. + */ +@interface RLMPermissionUser : RLMObject +/// The unique Realm Object Server user ID string identifying this user. This will have +/// the same value as `-[RLMSyncUser identity]`. +@property (nonatomic) NSString *identity; + +/// The user's private role. This will be initialized to a role named for the user's +/// identity that contains this user as its only member. +@property (nonatomic) RLMPermissionRole *role; + +/// Roles which this user belongs to. +@property (nonatomic, readonly) RLMLinkingObjects *roles; + +/// Get the user object in the given Realm, creating it if needed. ++ (RLMPermissionUser *)userInRealm:(RLMRealm *)realm withIdentity:(NSString *)identity; +@end + +/** + A singleton object which describes Realm-wide permissions. + + An object of this type is automatically created in the Realm for you, and more objects + cannot be created manually. Call `+[RLMRealmPermission objectInRealm:]` to obtain the + instance for a specific Realm. + + See `RLMRealmPrivileges` for the meaning of permissions applied to a Realm. + */ +@interface RLMRealmPermission : RLMObject +/// The permissions for the Realm. +@property (nonatomic) RLMArray *permissions; + +/// Retrieve the singleton object for the given Realm. This will return `nil` +/// for non-partial-sync Realms. ++ (nullable instancetype)objectInRealm:(RLMRealm *)realm; +@end + +/** + An object which describes class-wide permissions. + + An instance of this object is automatically created in the Realm for class in your schema, + and should not be created manually. Call `+[RLMClassPermission objectInRealm:forClassNamed:]` + or `+[RLMClassPermission objectInRealm:forClass:]` to obtain the existing instance, or + query `RLMClassPermission` as normal. + */ +@interface RLMClassPermission : RLMObject +/// The name of the class which these permissions apply to. +@property (nonatomic) NSString *name; +/// The permissions for this class. +@property (nonatomic) RLMArray *permissions; + +/// Retrieve the object for the named RLMObject subclass. This will return `nil` +/// for non-partial-sync Realms. ++ (nullable instancetype)objectInRealm:(RLMRealm *)realm forClassNamed:(NSString *)className; +/// Retrieve the object for the given RLMObject subclass. This will return `nil` +/// for non-partial-sync Realms. ++ (nullable instancetype)objectInRealm:(RLMRealm *)realm forClass:(Class)cls; +@end + +/** + A description of the actual privileges which apply to a Realm. + + This is a combination of all of the privileges granted to all of the Roles which the + current User is a member of, obtained by calling `-[RLMRealm privilegesForRealm]` on + the Realm. + + By default, all operations are permitted, and each privilege field indicates an operation + which may be forbidden. + */ +struct RLMRealmPrivileges { + /// If `false`, the current User is not permitted to see the Realm at all. This can + /// happen only if the Realm was created locally and has not yet been synchronized. + bool read : 1; + + /// If `false`, no modifications to the Realm are permitted. Write transactions can + /// be performed locally, but any changes made will be reverted by the server. + /// `setPermissions` and `modifySchema` will always be `false` when this is `false`. + bool update : 1; + + /// If `false`, no modifications to the permissions property of the RLMRealmPermissions + /// object for are permitted. Write transactions can be performed locally, but any + /// changes made will be reverted by the server. + /// + /// Note that if invalide privilege changes are made, `-[RLMRealm privilegesFor*:]` + /// will return results reflecting those invalid changes until synchronization occurs. + /// + /// Even if this field is `true`, note that the user will be unable to grant + /// privileges to a Role which they do not themselves have. + /// + /// Adding or removing Users from a Role is controlled by Update privileges on that + /// Role, and not by this value. + bool setPermissions : 1; + + /// If `false`, the user is not permitted to add new object types to the Realm or add + /// new properties to existing objec types. Defining new RLMObject subclasses (and not + /// excluding them from the schema with `-[RLMRealmConfiguration setObjectClasses:]`) + /// will result in the application crashing if the object types are not first added on + /// the server by a more privileged user. + bool modifySchema : 1; +}; + +/** + A description of the actual privileges which apply to a Class within a Realm. + + This is a combination of all of the privileges granted to all of the Roles which the + current User is a member of, obtained by calling `-[RLMRealm privilegesForClass:]` or + `-[RLMRealm privilegesForClassNamed:]` on the Realm. + + By default, all operations are permitted, and each privilege field indicates an operation + which may be forbidden. + */ +struct RLMClassPrivileges { + /// If `false`, the current User is not permitted to see objects of this type, and + /// attempting to query this class will always return empty results. + /// + /// Note that Read permissions are transitive, and so it may be possible to read an + /// object which the user does not directly have Read permissions for by following a + /// link to it from an object they do have Read permissions for. This does not apply + /// to any of the other permission types. + bool read : 1; + + /// If `false`, creating new objects of this type is not permitted. Write transactions + /// creating objects can be performed locally, but the objects will be deleted by the + /// server when synchronization occurs. + /// + /// For objects with Primary Keys, it may not be locally determinable if Create or + /// Update privileges are applicable. It may appear that you are creating a new object, + /// but an object with that Primary Key may already exist and simply not be visible to + /// you, in which case it is actually an Update operation. + bool create : 1; + + /// If `false`, no modifications to objects of this type are permitted. Write + /// transactions modifying the objects can be performed locally, but any changes made + /// will be reverted by the server. + /// + /// Deleting an object is considered a modification, and is governed by this privilege. + bool update : 1; + + /// If `false`, the User is not permitted to create new subscriptions for this class. + /// Local queries against the objects within the Realm will work, but new + /// subscriptions will never add objects to the Realm. + bool subscribe : 1; + + /// If `false`, no modifications to the permissions property of the RLMClassPermissions + /// object for this type are permitted. Write transactions can be performed locally, + /// but any changes made will be reverted by the server. + /// + /// Note that if invalid privilege changes are made, `-[RLMRealm privilegesFor*:]` + /// will return results reflecting those invalid changes until synchronization occurs. + /// + /// Even if this field is `true`, note that the user will be unable to grant + /// privileges to a Role which they do not themselves have. + bool setPermissions : 1; +}; + +/** + A description of the actual privileges which apply to a specific RLMObject. + + This is a combination of all of the privileges granted to all of the Roles which the + current User is a member of, obtained by calling `-[RLMRealm privilegesForObject:]` on + the Realm. + + By default, all operations are permitted, and each privilege field indicates an operation + which may be forbidden. + */ +struct RLMObjectPrivileges { + /// If `false`, the current User is not permitted to read this object directly. + /// + /// Objects which cannot be read by a user will appear in a Realm due to that read + /// permissions are transitive. All objects which a readable object links to are + /// themselves implicitly readable. If the link to an object with `read=false` is + /// removed, the object will be deleted from the local Realm. + bool read : 1; + + /// If `false`, modifying the fields of this type is not permitted. Write + /// transactions modifying the objects can be performed locally, but any changes made + /// will be reverted by the server. + /// + /// Note that even if this is `true`, the user may not be able to modify the + /// `RLMArray *` property of the object (if it exists), as that is + /// governed by `setPermissions`. + bool update : 1; + + /// If `false`, deleting this object is not permitted. Write transactions which delete + /// the object can be performed locally, but the server will restore it. + /// + /// It is possible to have `update` but not `delete` privileges, or vice versa. For + /// objects with primary keys, `delete` but not `update` is ill-advised, as an object + /// can be updated by deleting and recreating it. + bool del : 1; + + /// If `false`, modifying the privileges of this specific object is not permitted. + /// + /// Object-specific permissions are set by declaring a `RLMArray *` + /// property on the `RLMObject` subclass. Modifications to this property are + /// controlled by `setPermissions` rather than `update`. + /// + /// Even if this field is `true`, note that the user will be unable to grant + /// privileges to a Role which they do not themselves have. + bool setPermissions : 1; +}; + +/// :nodoc: +FOUNDATION_EXTERN id RLMPermissionForRole(RLMArray *array, id role); + +/** + Access levels which can be granted to Realm Mobile Platform users + for specific synchronized Realms, using the permissions APIs. + + Note that each access level guarantees all allowed actions provided + by less permissive access levels. Specifically, users with write + access to a Realm can always read from that Realm, and users with + administrative access can always read or write from the Realm. + */ +typedef NS_ENUM(NSUInteger, RLMSyncAccessLevel) { + /// No access whatsoever. + RLMSyncAccessLevelNone = 0, + /** + User can only read the contents of the Realm. + + @warning Users who have read-only access to a Realm should open the + Realm using `+[RLMRealm asyncOpenWithConfiguration:callbackQueue:callback:]`. + Attempting to directly open the Realm is an error; in this + case the Realm must be deleted and re-opened. + */ + RLMSyncAccessLevelRead = 1, + /// User can read and write the contents of the Realm. + RLMSyncAccessLevelWrite = 2, + /// User can read, write, and administer the Realm, including + /// granting permissions to other users. + RLMSyncAccessLevelAdmin = 3, +}; + +/** + A property on which a `RLMResults` can be queried or filtered. + + @warning If building `NSPredicate`s using format strings including these string + constants, use %K instead of %@ as the substitution parameter. + */ +typedef NSString * RLMSyncPermissionSortProperty NS_STRING_ENUM; + +/// Sort by the Realm Object Server path to the Realm to which the permission applies. +extern RLMSyncPermissionSortProperty const RLMSyncPermissionSortPropertyPath; +/// Sort by the identity of the user to whom the permission applies. +extern RLMSyncPermissionSortProperty const RLMSyncPermissionSortPropertyUserID; +/// Sort by the date the permissions were last updated. +extern RLMSyncPermissionSortProperty const RLMSyncPermissionSortPropertyUpdated; + +/** + A value representing a permission granted to the specified user(s) to access the specified Realm(s). + + `RLMSyncPermission` is immutable and can be accessed from any thread. + + See https://realm.io/docs/realm-object-server/#permissions for general documentation. + */ +@interface RLMSyncPermission : NSObject + +/** + The Realm Object Server path to the Realm to which this permission applies (e.g. "/path/to/realm"). + + Specify "*" if this permission applies to all Realms managed by the server. + */ +@property (nonatomic, readonly) NSString *path; + +/** + The access level described by this permission. + */ +@property (nonatomic, readonly) RLMSyncAccessLevel accessLevel; + +/// Whether the access level allows the user to read from the Realm. +@property (nonatomic, readonly) BOOL mayRead; + +/// Whether the access level allows the user to write to the Realm. +@property (nonatomic, readonly) BOOL mayWrite; + +/// Whether the access level allows the user to administer the Realm. +@property (nonatomic, readonly) BOOL mayManage; + +/** + Create a new sync permission value, for use with permission APIs. + + @param path The Realm Object Server path to the Realm whose permission should be modified + (e.g. "/path/to/realm"). Pass "*" to apply to all Realms managed by the user. + @param identity The Realm Object Server identity of the user who should be granted access to + the Realm at `path`. + Pass "*" to apply to all users managed by the server. + @param accessLevel The access level to grant. + */ +- (instancetype)initWithRealmPath:(NSString *)path + identity:(NSString *)identity + accessLevel:(RLMSyncAccessLevel)accessLevel; + +/** + Create a new sync permission value, for use with permission APIs. + + @param path The Realm Object Server path to the Realm whose permission should be modified + (e.g. "/path/to/realm"). Pass "*" to apply to all Realms managed by the user. + @param username The username (often an email address) of the user who should be granted access + to the Realm at `path`. + @param accessLevel The access level to grant. + */ +- (instancetype)initWithRealmPath:(NSString *)path + username:(NSString *)username + accessLevel:(RLMSyncAccessLevel)accessLevel; + +/** + The identity of the user to whom this permission is granted, or "*" + if all users are granted this permission. Nil if the permission is + defined in terms of a key-value pair. + */ +@property (nullable, nonatomic, readonly) NSString *identity; + +/** + If the permission is defined in terms of a key-value pair, the key + describing the type of criterion used to determine what users the + permission applies to. Otherwise, nil. + */ +@property (nullable, nonatomic, readonly) NSString *key; + +/** + If the permission is defined in terms of a key-value pair, a string + describing the criterion value used to determine what users the + permission applies to. Otherwise, nil. + */ +@property (nullable, nonatomic, readonly) NSString *value; + +/** + When this permission was last updated. + */ +@property (nonatomic, readonly) NSDate *updatedAt; + +/// :nodoc: +- (instancetype)init __attribute__((unavailable("Use the designated initializer"))); + +/// :nodoc: ++ (instancetype)new __attribute__((unavailable("Use the designated initializer"))); + +// MARK: - Migration assistance + +/// :nodoc: +@property (nullable, nonatomic, readonly) NSString *userId __attribute__((unavailable("Renamed to `identity`"))); + +/// :nodoc: +- (instancetype)initWithRealmPath:(NSString *)path + userID:(NSString *)identity + accessLevel:(RLMSyncAccessLevel)accessLevel +__attribute__((unavailable("Renamed to `-initWithRealmPath:identity:accessLevel:`"))); + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMSyncPermissionChange.h b/Pods/Realm/include/RLMSyncPermissionChange.h deleted file mode 100644 index 2b19da1..0000000 --- a/Pods/Realm/include/RLMSyncPermissionChange.h +++ /dev/null @@ -1,91 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#import -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@class RLMSyncUser; - -/** - This model is used for requesting changes to a Realm's permissions. - - It should be used in conjunction with an `RLMSyncUser`'s management Realm. - - See https://realm.io/docs/realm-object-server/#permissions for general - documentation. - */ -@interface RLMSyncPermissionChange : RLMObject - -/// The globally unique ID string of this permission change object. -@property (readonly) NSString *id; - -/// The date this object was initially created. -@property (readonly) NSDate *createdAt; - -/// The date this object was last modified. -@property (readonly) NSDate *updatedAt; - -/// The status code of the object that was processed by Realm Object Server. -@property (nullable, readonly) NSNumber *statusCode; - -/// An error or informational message, typically written to by the Realm Object Server. -@property (nullable, readonly) NSString *statusMessage; - -/// Sync management object status. -@property (readonly) RLMSyncManagementObjectStatus status; - -/// The remote URL to the realm. -@property (readonly) NSString *realmUrl; - -/// The identity of a user affected by this permission change. -@property (readonly) NSString *userId; - -/// Define read access. Set to `YES` or `NO` to update this value. Leave unset to preserve the existing setting. -@property (nullable, readonly) NSNumber *mayRead; -/// Define write access. Set to `YES` or `NO` to update this value. Leave unset to preserve the existing setting. -@property (nullable, readonly) NSNumber *mayWrite; -/// Define management access. Set to `YES` or `NO` to update this value. Leave unset to preserve the existing setting. -@property (nullable, readonly) NSNumber *mayManage; - -/** - Construct a permission change object used to change the access permissions for a user on a Realm. - - @param realmURL The Realm URL whose permissions settings should be changed. - Use `*` to change the permissions of all Realms managed by the management Realm's `RLMSyncUser`. - @param userID The user or users who should be granted these permission changes. - Use `*` to change the permissions for all users. - @param mayRead Define read access. Set to `YES` or `NO` to update this value. - Leave unset to preserve the existing setting. - @param mayWrite Define write access. Set to `YES` or `NO` to update this value. - Leave unset to preserve the existing setting. - @param mayManage Define management access. Set to `YES` or `NO` to update this value. - Leave unset to preserve the existing setting. - */ -+ (instancetype)permissionChangeWithRealmURL:(NSString *)realmURL - userID:(NSString *)userID - read:(nullable NSNumber *)mayRead - write:(nullable NSNumber *)mayWrite - manage:(nullable NSNumber *)mayManage; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMSyncPermissionOffer.h b/Pods/Realm/include/RLMSyncPermissionOffer.h deleted file mode 100644 index 29cd3fd..0000000 --- a/Pods/Realm/include/RLMSyncPermissionOffer.h +++ /dev/null @@ -1,91 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#import -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -/** - This model is used for offering permission changes to other users. - - It should be used in conjunction with an `RLMSyncUser`'s management Realm. - - See https://realm.io/docs/realm-object-server/#permissions for general - documentation. - */ -@interface RLMSyncPermissionOffer : RLMObject - -/// The globally unique ID string of this permission offer object. -@property (readonly) NSString *id; - -/// The date this object was initially created. -@property (readonly) NSDate *createdAt; - -/// The date this object was last modified. -@property (readonly) NSDate *updatedAt; - -/// The status code of the object that was processed by Realm Object Server. -@property (nullable, readonly) NSNumber *statusCode; - -/// An error or informational message, typically written to by the Realm Object Server. -@property (nullable, readonly) NSString *statusMessage; - -/// Sync management object status. -@property (readonly) RLMSyncManagementObjectStatus status; - -/// A token which uniquely identifies this offer. Generated by the server. -@property (nullable, readonly) NSString *token; - -/// The remote URL to the realm. -@property (readonly) NSString *realmUrl; - -/// Whether this offer allows the receiver to read from the Realm. -@property (readonly) BOOL mayRead; - -/// Whether this offer allows the receiver to write to the Realm. -@property (readonly) BOOL mayWrite; - -/// Whether this offer allows the receiver to manage the access rights for others. -@property (readonly) BOOL mayManage; - -/// When this token will expire and become invalid. -@property (nullable, readonly) NSDate *expiresAt; - -/** - Construct a permission offer object used to offer permission changes to other users. - - @param realmURL The URL to the Realm on which to apply these permission changes - to, once the offer is accepted. - @param expiresAt When this token will expire and become invalid. - Pass `nil` if this offer should not expire. - @param mayRead Grant or revoke read access. - @param mayWrite Grant or revoked read-write access. - @param mayManage Grant or revoke administrative access. - */ -+ (instancetype)permissionOfferWithRealmURL:(NSString *)realmURL - expiresAt:(nullable NSDate *)expiresAt - read:(BOOL)mayRead - write:(BOOL)mayWrite - manage:(BOOL)mayManage; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMSyncPermissionOfferResponse.h b/Pods/Realm/include/RLMSyncPermissionOfferResponse.h deleted file mode 100644 index 575bc1a..0000000 --- a/Pods/Realm/include/RLMSyncPermissionOfferResponse.h +++ /dev/null @@ -1,73 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -/** - This model is used to apply permission changes defined in the permission offer - object represented by the specified token, which was created by another user's - `RLMSyncPermissionOffer` object. - - It should be used in conjunction with an `RLMSyncUser`'s management Realm. - - See https://realm.io/docs/realm-object-server/#permissions for general - documentation. - */ -@interface RLMSyncPermissionOfferResponse : RLMObject - -/// The globally unique ID string of this permission offer response object. -@property (readonly) NSString *id; - -/// The date this object was initially created. -@property (readonly) NSDate *createdAt; - -/// The date this object was last modified. -@property (readonly) NSDate *updatedAt; - -/// The status code of the object that was processed by Realm Object Server. -@property (nullable, readonly) NSNumber *statusCode; - -/// An error or informational message, typically written to by the Realm Object Server. -@property (nullable, readonly) NSString *statusMessage; - -/// Sync management object status. -@property (readonly) RLMSyncManagementObjectStatus status; - -/// The received token which uniquely identifies another user's `RLMSyncPermissionOffer`. -@property (readonly) NSString *token; - -/// The remote URL to the realm on which these permission changes were applied. -/// Generated by the server. -@property (nullable, readonly) NSString *realmUrl; - -/** - Construct a permission offer response object used to apply permission changes - defined in the permission offer object represented by the specified token, - which was created by another user's `RLMSyncPermissionOffer` object. - - @param token The received token which uniquely identifies another user's - `RLMSyncPermissionOffer`. - */ -+ (instancetype)permissionOfferResponseWithToken:(NSString *)token; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMSyncErrorResponseModel.h b/Pods/Realm/include/RLMSyncPermissionResults.h similarity index 61% rename from Pods/Realm/include/RLMSyncErrorResponseModel.h rename to Pods/Realm/include/RLMSyncPermissionResults.h index 930a7ea..461da2b 100644 --- a/Pods/Realm/include/RLMSyncErrorResponseModel.h +++ b/Pods/Realm/include/RLMSyncPermissionResults.h @@ -1,6 +1,6 @@ //////////////////////////////////////////////////////////////////////////// // -// Copyright 2016 Realm Inc. +// Copyright 2017 Realm Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -17,20 +17,11 @@ //////////////////////////////////////////////////////////////////////////// #import -#import "RLMSyncUtil_Private.h" -NS_ASSUME_NONNULL_BEGIN +#import "RLMResults.h" -@interface RLMSyncErrorResponseModel : NSObject RLM_SYNC_UNINITIALIZABLE - -@property (nonatomic, readonly, assign) NSInteger status; -@property (nonatomic, readonly, assign) NSInteger code; -@property (nonatomic, readonly, copy) NSString *title; -@property (nonatomic, readonly, copy) NSString *hint; - -- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary; +@class RLMSyncPermission; +// A private subclass of `RLMResults`. +@interface RLMSyncPermissionResults : RLMResults @end - - -NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMSyncPermissionChange_Private.h b/Pods/Realm/include/RLMSyncPermission_Private.hpp similarity index 52% rename from Pods/Realm/include/RLMSyncPermissionChange_Private.h rename to Pods/Realm/include/RLMSyncPermission_Private.hpp index df632bc..9d01adc 100644 --- a/Pods/Realm/include/RLMSyncPermissionChange_Private.h +++ b/Pods/Realm/include/RLMSyncPermission_Private.hpp @@ -1,6 +1,6 @@ //////////////////////////////////////////////////////////////////////////// // -// Copyright 2016 Realm Inc. +// Copyright 2017 Realm Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,24 +16,14 @@ // //////////////////////////////////////////////////////////////////////////// -#import "RLMSyncPermissionChange.h" +#import "RLMSyncPermission.h" -NS_ASSUME_NONNULL_BEGIN +#import "sync/sync_permission.hpp" -@interface RLMSyncPermissionChange() +@interface RLMSyncPermission () -@property (readwrite) NSString *id; -@property (readwrite) NSDate *createdAt; -@property (readwrite) NSDate *updatedAt; -@property (nullable, readwrite) NSNumber *statusCode; -@property (nullable, readwrite) NSString *statusMessage; -@property (readwrite) NSString *realmUrl; -@property (readwrite) NSString *userId; +- (instancetype)initWithPermission:(realm::Permission)permission; -@property (nullable, readwrite) NSNumber *mayRead; -@property (nullable, readwrite) NSNumber *mayWrite; -@property (nullable, readwrite) NSNumber *mayManage; +- (realm::Permission)rawPermission; @end - -NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMSyncSession.h b/Pods/Realm/include/RLMSyncSession.h index 8f21d20..436d300 100644 --- a/Pods/Realm/include/RLMSyncSession.h +++ b/Pods/Realm/include/RLMSyncSession.h @@ -21,7 +21,7 @@ #import "RLMRealm.h" /** - The current state of the session represented by an `RLMSyncSession` object. + The current state of the session represented by a session object. */ typedef NS_ENUM(NSUInteger, RLMSyncSessionState) { /// The sync session is bound to the Realm Object Server and communicating with it. @@ -51,15 +51,15 @@ typedef NS_ENUM(NSUInteger, RLMSyncProgressDirection) { Progress notification blocks can be registered on sessions if your app wishes to be informed how many bytes have been uploaded or downloaded, for example to show progress indicator UIs. */ -typedef NS_ENUM(NSUInteger, RLMSyncProgress) { +typedef NS_ENUM(NSUInteger, RLMSyncProgressMode) { /** - The block will be called forever, or until it is unregistered by calling - `-[RLMProgressNotificationToken stop]`. + The block will be called indefinitely, or until it is unregistered by calling + `-[RLMProgressNotificationToken invalidate]`. Notifications will always report the latest number of transferred bytes, and the most up-to-date number of total transferrable bytes. */ - RLMSyncProgressReportIndefinitely, + RLMSyncProgressModeReportIndefinitely, /** The block will, upon registration, store the total number of bytes to be transferred. When invoked, it will always report the most up-to-date number @@ -68,10 +68,10 @@ typedef NS_ENUM(NSUInteger, RLMSyncProgress) { When the number of transferred bytes reaches or exceeds the number of transferrable bytes, the block will be unregistered. */ - RLMSyncProgressForCurrentlyOutstandingWork, + RLMSyncProgressModeForCurrentlyOutstandingWork, }; -@class RLMSyncUser, RLMSyncConfiguration; +@class RLMSyncUser, RLMSyncConfiguration, RLMSyncErrorActionToken; /** The type of a progress notification block intended for reporting a session's network @@ -85,9 +85,9 @@ typedef void(^RLMProgressNotificationBlock)(NSUInteger transferredBytes, NSUInte NS_ASSUME_NONNULL_BEGIN /** - A token object corresponding to a progress notification block on an `RLMSyncSession`. + A token object corresponding to a progress notification block on a session object. - To stop notifications manually, call `-stop` on it. Notifications should be stopped before + To stop notifications manually, call `-invalidate` on it. Notifications should be stopped before the token goes out of scope or is destroyed. */ @interface RLMProgressNotificationToken : RLMNotificationToken @@ -98,8 +98,9 @@ NS_ASSUME_NONNULL_BEGIN communication between the client (and a local Realm file on disk), and the server (and a remote Realm at a given URL stored on a Realm Object Server). - Sessions are always created by the SDK and vended out through various APIs. The lifespans - of sessions associated with Realms are managed automatically. + Sessions are always created by the SDK and vended out through various APIs. The + lifespans of sessions associated with Realms are managed automatically. Session + objects can be accessed from any thread. */ @interface RLMSyncSession : NSObject @@ -123,13 +124,13 @@ NS_ASSUME_NONNULL_BEGIN Multiple blocks can be registered with the same session at once. Each block will be invoked on a side queue devoted to progress notifications. - + If the session has already received progress information from the synchronization subsystem, the block will be called immediately. Otherwise, it will be called as soon as progress information becomes available. The token returned by this method must be retained as long as progress - notifications are desired, and the `-stop` method should be called on it + notifications are desired, and the `-invalidate` method should be called on it when notifications are no longer needed and before the token is destroyed. If no token is returned, the notification block will never be called again. @@ -149,10 +150,43 @@ NS_ASSUME_NONNULL_BEGIN @see `RLMSyncProgressDirection`, `RLMSyncProgress`, `RLMProgressNotificationBlock`, `RLMProgressNotificationToken` */ - (nullable RLMProgressNotificationToken *)addProgressNotificationForDirection:(RLMSyncProgressDirection)direction - mode:(RLMSyncProgress)mode + mode:(RLMSyncProgressMode)mode block:(RLMProgressNotificationBlock)block NS_REFINED_FOR_SWIFT; +/** + Given an error action token, immediately handle the corresponding action. + + @see `RLMSyncErrorClientResetError`, `RLMSyncErrorPermissionDeniedError` + */ ++ (void)immediatelyHandleError:(RLMSyncErrorActionToken *)token; + +/** + Get the sync session for the given Realm if it is a synchronized Realm, or `nil` + if it is not. + */ ++ (nullable RLMSyncSession *)sessionForRealm:(RLMRealm *)realm; + +@end + +// MARK: - Error action token + +#pragma mark - Error action token + +/** + An opaque token returned as part of certain errors. It can be + passed into certain APIs to perform certain actions. + + @see `RLMSyncErrorClientResetError`, `RLMSyncErrorPermissionDeniedError` + */ +@interface RLMSyncErrorActionToken : NSObject + +/// :nodoc: +- (instancetype)init __attribute__((unavailable("This type cannot be created directly"))); + +/// :nodoc: ++ (instancetype)new __attribute__((unavailable("This type cannot be created directly"))); + @end NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMSyncSessionRefreshHandle.hpp b/Pods/Realm/include/RLMSyncSessionRefreshHandle.hpp index fd251b7..26b5bea 100644 --- a/Pods/Realm/include/RLMSyncSessionRefreshHandle.hpp +++ b/Pods/Realm/include/RLMSyncSessionRefreshHandle.hpp @@ -24,6 +24,7 @@ namespace realm { class SyncSession; +class SyncUser; } @class RLMSyncUser; @@ -33,7 +34,7 @@ class SyncSession; NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithRealmURL:(NSURL *)realmURL - user:(RLMSyncUser *)user + user:(std::shared_ptr)user session:(std::shared_ptr)session completionBlock:(nullable RLMSyncBasicErrorReportingBlock)completionBlock; diff --git a/Pods/Realm/include/RLMSyncSession_Private.hpp b/Pods/Realm/include/RLMSyncSession_Private.hpp index 10a39ea..1f691cb 100644 --- a/Pods/Realm/include/RLMSyncSession_Private.hpp +++ b/Pods/Realm/include/RLMSyncSession_Private.hpp @@ -35,10 +35,16 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithSyncSession:(std::shared_ptr)session; /// Wait for pending uploads to complete or the session to expire, and dispatch the callback onto the specified queue. -- (BOOL)waitForUploadCompletionOnQueue:(nullable dispatch_queue_t)queue callback:(void(^)(void))callback; +- (BOOL)waitForUploadCompletionOnQueue:(nullable dispatch_queue_t)queue callback:(void(^)(NSError * _Nullable))callback; /// Wait for pending downloads to complete or the session to expire, and dispatch the callback onto the specified queue. -- (BOOL)waitForDownloadCompletionOnQueue:(nullable dispatch_queue_t)queue callback:(void(^)(void))callback; +- (BOOL)waitForDownloadCompletionOnQueue:(nullable dispatch_queue_t)queue callback:(void(^)(NSError * _Nullable))callback; + +@end + +@interface RLMSyncErrorActionToken () + +- (instancetype)initWithOriginalPath:(std::string)originalPath; @end diff --git a/Pods/Realm/include/RLMSyncSubscription.h b/Pods/Realm/include/RLMSyncSubscription.h new file mode 100644 index 0000000..25a2278 --- /dev/null +++ b/Pods/Realm/include/RLMSyncSubscription.h @@ -0,0 +1,138 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2018 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + `RLMSyncSubscriptionState` is an enumeration representing the possible state of a sync subscription. + */ +typedef NS_ENUM(NSInteger, RLMSyncSubscriptionState) { + /** + An error occurred while creating the subscription or while the server was processing it. + */ + RLMSyncSubscriptionStateError = -1, + + /** + The subscription is being created, but has not yet been written to the synced Realm. + */ + RLMSyncSubscriptionStateCreating = 2, + + /** + The subscription has been created, and is waiting to be processed by the server. + */ + RLMSyncSubscriptionStatePending = 0, + + /** + The subscription has been processed by the server, and objects matching the subscription + are now being synchronized to this client. + */ + RLMSyncSubscriptionStateComplete = 1, + + /** + This subscription has been removed. + */ + RLMSyncSubscriptionStateInvalidated = 3, +}; + +/** + `RLMSyncSubscription` represents a subscription to a set of objects in a synced Realm. + + When partial sync is enabled for a synced Realm, the only objects that the server synchronizes to the + client are those that match a sync subscription registered by that client. A subscription consists of + of a query (represented by an `RLMResults`) and an optional name. + + The state of the subscription can be observed using [Key-Value Observing](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html) on the `state` property. + + Subscriptions are created using `-[RLMResults subscribe]` or `-[RLMResults subscribeWithName:]`. + */ +@interface RLMSyncSubscription : NSObject + +/** + The unique name for this subscription. + + This will be `nil` if a name was not provided when the subscription was created. + */ +@property (nonatomic, readonly, nullable) NSString *name; + +/** + The state of the subscription. See `RLMSyncSubscriptionState`. + */ +@property (nonatomic, readonly) RLMSyncSubscriptionState state; + +/** + The error associated with this subscription, if any. + + Will be non-nil only when `state` is `RLMSyncSubscriptionStateError`. + */ +@property (nonatomic, readonly, nullable) NSError *error; + +/** + Remove this subscription. + + Removing a subscription will delete all objects from the local Realm that were matched + only by that subscription and not any remaining subscriptions. The deletion is performed + by the server, and so has no immediate impact on the contents of the local Realm. If the + device is currently offline, the removal will not be processed until the device returns online. + */ +- (void)unsubscribe; + +#pragma mark - Unavailable Methods + +/** + `-[RLMSyncSubscription init]` is not available because `RLMSyncSubscription` cannot be created directly. + */ +- (instancetype)init __attribute__((unavailable("RLMSyncSubscription cannot be created directly"))); + +/** + `+[RLMSyncSubscription new]` is not available because `RLMSyncSubscription` cannot be created directly. + */ ++ (instancetype)new __attribute__((unavailable("RLMSyncSubscription cannot be created directly"))); + +@end + +/** + Support for subscribing to the results of object queries in a synced Realm. + */ +@interface RLMResults (SyncSubscription) + +/** + Subscribe to the query represented by this `RLMResults`. + + The subscription will not be explicitly named. + + @return The subscription + + @see RLMSyncSubscription +*/ +- (RLMSyncSubscription *)subscribe; + +/** + Subscribe to the query represented by this `RLMResults`. + + @param subscriptionName The name of the subscription + + @return The subscription + + @see RLMSyncSubscription +*/ +- (RLMSyncSubscription *)subscribeWithName:(NSString *)subscriptionName; +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMSyncUser.h b/Pods/Realm/include/RLMSyncUser.h index 54cc79b..c8f16ff 100644 --- a/Pods/Realm/include/RLMSyncUser.h +++ b/Pods/Realm/include/RLMSyncUser.h @@ -18,7 +18,12 @@ #import -@class RLMSyncUser, RLMSyncCredentials, RLMSyncSession, RLMRealm; +#import "RLMRealmConfiguration.h" +#import "RLMResults.h" +#import "RLMSyncCredentials.h" +#import "RLMSyncPermission.h" + +@class RLMSyncUser, RLMSyncUserInfo, RLMSyncCredentials, RLMSyncPermission, RLMSyncSession, RLMRealm; /** The state of the user object. @@ -35,20 +40,47 @@ typedef NS_ENUM(NSUInteger, RLMSyncUserState) { /// A block type used for APIs which asynchronously vend an `RLMSyncUser`. typedef void(^RLMUserCompletionBlock)(RLMSyncUser * _Nullable, NSError * _Nullable); +/// A block type used to report the status of a password change operation. +/// If the `NSError` argument is nil, the operation succeeded. +typedef void(^RLMPasswordChangeStatusBlock)(NSError * _Nullable); + +/// A block type used to report the status of a permission apply or revoke operation. +/// If the `NSError` argument is nil, the operation succeeded. +typedef void(^RLMPermissionStatusBlock)(NSError * _Nullable); + +/// A block type used to report the status of a permission offer operation. +typedef void(^RLMPermissionOfferStatusBlock)(NSString * _Nullable, NSError * _Nullable); + +/// A block type used to report the status of a permission offer response operation. +typedef void(^RLMPermissionOfferResponseStatusBlock)(NSURL * _Nullable, NSError * _Nullable); + +/// A block type used to asynchronously report results of a permissions get operation. +/// Exactly one of the two arguments will be populated. +typedef void(^RLMPermissionResultsBlock)(RLMResults * _Nullable, NSError * _Nullable); + +/// A block type used to asynchronously report results of a user info retrieval. +/// Exactly one of the two arguments will be populated. +typedef void(^RLMRetrieveUserBlock)(RLMSyncUserInfo * _Nullable, NSError * _Nullable); + +/// A block type used to report an error related to a specific user. +typedef void(^RLMUserErrorReportingBlock)(RLMSyncUser * _Nonnull, NSError * _Nonnull); + NS_ASSUME_NONNULL_BEGIN /** - A `RLMSyncUser` instance represents a single Realm Object Server user account (or just user). + A `RLMSyncUser` instance represents a single Realm Object Server user account. - A user may have one or more credentials associated with it. These credentials uniquely identify the user to a - third-party auth provider, and are used to sign into a Realm Object Server user account. + A user may have one or more credentials associated with it. These credentials + uniquely identify the user to the authentication provider, and are used to sign + into a Realm Object Server user account. - Note that users are only vended out via SDK APIs, and only one user instance ever exists for a given user account. + Note that user objects are only vended out via SDK APIs, and cannot be directly + initialized. User objects can be accessed from any thread. */ @interface RLMSyncUser : NSObject /** - A dictionary of all valid, logged-in user identities corresponding to their `RLMSyncUser` objects. + A dictionary of all valid, logged-in user identities corresponding to their user objects. */ + (NSDictionary *)allUsers NS_REFINED_FOR_SWIFT; @@ -69,41 +101,142 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nullable, nonatomic, readonly) NSURL *authenticationServer; +/** + Whether the user is a Realm Object Server administrator. Value reflects the + state at the time of the last successful login of this user. + */ +@property (nonatomic, readonly) BOOL isAdmin; + /** The current state of the user. */ @property (nonatomic, readonly) RLMSyncUserState state; +#pragma mark - Lifecycle + /** - Create, log in, and asynchronously return a new user object, specifying a custom timeout for the network request. - Credentials identifying the user must be passed in. The user becomes available in the completion block, at which point - it is ready for use. + Create, log in, and asynchronously return a new user object, specifying a custom + timeout for the network request and a custom queue to run the callback upon. + Credentials identifying the user must be passed in. The user becomes available in + the completion block, at which point it is ready for use. */ + (void)logInWithCredentials:(RLMSyncCredentials *)credentials authServerURL:(NSURL *)authServerURL timeout:(NSTimeInterval)timeout + callbackQueue:(dispatch_queue_t)callbackQueue onCompletion:(RLMUserCompletionBlock)completion NS_REFINED_FOR_SWIFT; /** - Create, log in, and asynchronously return a new user object. Credentials identifying the user must be passed in. The - user becomes available in the completion block, at which point it is ready for use. + Create, log in, and asynchronously return a new user object. + + If the login completes successfully, the completion block will invoked with + a `RLMSyncUser` object representing the logged-in user. This object can be + used to open synchronized Realms. If the login fails, the completion block + will be invoked with an error. + + The completion block always runs on the main queue. + + @param credentials A credentials value identifying the user to be logged in. + @param authServerURL The URL of the authentication server (e.g. "http://realm.example.org:9080"). + @param completion A callback block that returns a user object or an error, + indicating the completion of the login operation. */ + (void)logInWithCredentials:(RLMSyncCredentials *)credentials authServerURL:(NSURL *)authServerURL onCompletion:(RLMUserCompletionBlock)completion NS_SWIFT_UNAVAILABLE("Use the full version of this API."); + +/** + Returns the default configuration for the user. The default configuration + points to the default query-based Realm on the server the user authenticated against. + */ +- (RLMRealmConfiguration *)configuration NS_REFINED_FOR_SWIFT; + /** - Log a user out, destroying their server state, deregistering them from the SDK, and removing any synced Realms - associated with them from on-disk storage. If the user is already logged out or in an error state, this is a no-op. + Create a query-based configuration instance for the given url. - This method should be called whenever the application is committed to not using a user again unless they are recreated. - Failing to call this method may result in unused files and metadata needlessly taking up space. + @param url The unresolved absolute URL to the Realm on the Realm Object Server, + e.g. "realm://example.org/~/path/to/realm". "Unresolved" means the + path should contain the wildcard marker `~`, which will automatically + be filled in with the user identity by the Realm Object Server. + @return A default configuration object with the sync configuration set to use the given URL. + */ +- (RLMRealmConfiguration *)configurationWithURL:(nullable NSURL *)url NS_REFINED_FOR_SWIFT; + +/** + Create a configuration instance for the given url. + + @param url The unresolved absolute URL to the Realm on the Realm Object Server, + e.g. "realm://example.org/~/path/to/realm". "Unresolved" means the + path should contain the wildcard marker `~`, which will automatically + be filled in with the user identity by the Realm Object Server. + @param fullSynchronization If YES, all objects in the server Realm are + automatically synchronized, and the query subscription + methods cannot be used. + @return A default configuration object with the sync configuration set to use + the given URL and options. + */ +- (RLMRealmConfiguration *)configurationWithURL:(nullable NSURL *)url + fullSynchronization:(bool)fullSynchronization NS_REFINED_FOR_SWIFT; + +/** + Create a configuration instance for the given url. + + @param url The unresolved absolute URL to the Realm on the Realm Object Server, + e.g. "realm://example.org/~/path/to/realm". "Unresolved" means the + path should contain the wildcard marker `~`, which will automatically + be filled in with the user identity by the Realm Object Server. + @param fullSynchronization If YES, all objects in the server Realm are + automatically synchronized, and the query subscription + methods cannot be used. + @param enableSSLValidation If NO, invalid SSL certificates for the server will + not be rejected. THIS SHOULD NEVER BE USED IN + PRODUCTION AND EXISTS ONLY FOR TESTING PURPOSES. + @param urlPrefix A prefix which is prepending to URLs constructed for + the server. This should normally be `nil`, and customized only + to match corresponding settings on the server. + @return A default configuration object with the sync configuration set to use + the given URL and options. + */ +- (RLMRealmConfiguration *)configurationWithURL:(nullable NSURL *)url + fullSynchronization:(bool)fullSynchronization + enableSSLValidation:(bool)enableSSLValidation + urlPrefix:(nullable NSString *)urlPrefix NS_REFINED_FOR_SWIFT; + +/** + Log a user out, destroying their server state, unregistering them from the SDK, + and removing any synced Realms associated with them from on-disk storage on + next app launch. If the user is already logged out or in an error state, this + method does nothing. + + This method should be called whenever the application is committed to not using + a user again unless they are recreated. + Failing to call this method may result in unused files and metadata needlessly + taking up space. */ - (void)logOut; /** - Retrieve a valid session object belonging to this user for a given URL, or `nil` if no such object exists. + An optional error handler which can be set to notify the host application when + the user encounters an error. Errors reported by this error handler are always + `RLMSyncAuthError`s. + + @note Check for `RLMSyncAuthErrorInvalidAccessToken` to see if the user has + been remotely logged out because its refresh token expired, or because the + third party authentication service providing the user's identity has + logged the user out. + + @warning Regardless of whether an error handler is installed, certain user errors + will automatically cause the user to enter the logged out state. + */ +@property (nullable, nonatomic) RLMUserErrorReportingBlock errorHandler NS_REFINED_FOR_SWIFT; + +#pragma mark - Sessions + +/** + Retrieve a valid session object belonging to this user for a given URL, or `nil` + if no such object exists. */ - (nullable RLMSyncSession *)sessionForURL:(NSURL *)url; @@ -112,20 +245,274 @@ NS_SWIFT_UNAVAILABLE("Use the full version of this API."); */ - (NSArray *)allSessions; +#pragma mark - Passwords + +/** + Change this user's password asynchronously. + + @warning Changing a user's password using an authentication server that doesn't + use HTTPS is a major security flaw, and should only be done while + testing. + + @param newPassword The user's new password. + @param completion Completion block invoked when login has completed or failed. + The callback will be invoked on a background queue provided + by `NSURLSession`. + */ +- (void)changePassword:(NSString *)newPassword completion:(RLMPasswordChangeStatusBlock)completion; + +/** + Change an arbitrary user's password asynchronously. + + @note The current user must be an admin user for this operation to succeed. + + @warning Changing a user's password using an authentication server that doesn't + use HTTPS is a major security flaw, and should only be done while + testing. + + @param newPassword The user's new password. + @param userID The identity of the user whose password should be changed. + @param completion Completion block invoked when login has completed or failed. + The callback will be invoked on a background queue provided + by `NSURLSession`. + */ +- (void)changePassword:(NSString *)newPassword forUserID:(NSString *)userID completion:(RLMPasswordChangeStatusBlock)completion; + +/** + Ask the server to send a password reset email to the given email address. + + If `email` is an email address which is associated with a user account that was + registered using the "password" authentication service, the server will send an + email to that address with a password reset token. No error is reported if the + email address is invalid or not associated with an account. + + @param serverURL The authentication server URL for the user. + @param email The email address to send the email to. + @param completion A block which will be called when the request completes or + fails. The callback will be invoked on a background queue + provided by `NSURLSession`, and not on the calling queue. + */ ++ (void)requestPasswordResetForAuthServer:(NSURL *)serverURL + userEmail:(NSString *)email + completion:(RLMPasswordChangeStatusBlock)completion; + +/** + Change a user's password using a one-time password reset token. + + By default, the password reset email sent by ROS will link to a web site where + the user can select a new password, and the app will not need to call this + method. If you wish to instead handle this within your native app, you must + change the `baseURL` in the server configuration for `PasswordAuthProvider` to + a scheme registered for your app, extract the token from the URL, and call this + method after prompting the user for a new password. + + @warning Changing a user's password using an authentication server that doesn't + use HTTPS is a major security flaw, and should only be done while + testing. + + @param serverURL The authentication server URL for the user. + @param token The one-time use token from the URL. + @param newPassword The user's new password. + @param completion A block which will be called when the request completes or + fails. The callback will be invoked on a background queue + provided by `NSURLSession`, and not on the calling queue. + */ ++ (void)completePasswordResetForAuthServer:(NSURL *)serverURL + token:(NSString *)token + password:(NSString *)newPassword + completion:(RLMPasswordChangeStatusBlock)completion; + +/** + Ask the server to send a confirmation email to the given email address. + + If `email` is an email address which is associated with a user account that was + registered using the "password" authentication service, the server will send an + email to that address with a confirmation token. No error is reported if the + email address is invalid or not associated with an account. + + @param serverURL The authentication server URL for the user. + @param email The email address to send the email to. + @param completion A block which will be called when the request completes or + fails. The callback will be invoked on a background queue + provided by `NSURLSession`, and not on the calling queue. + */ ++ (void)requestEmailConfirmationForAuthServer:(NSURL *)serverURL + userEmail:(NSString *)email + completion:(RLMPasswordChangeStatusBlock)completion; + +/** + Confirm a user's email using a one-time confirmation token. + + By default, the confirmation email sent by ROS will link to a web site with + a generic "thank you for confirming your email" message, and the app will not + need to call this method. If you wish to instead handle this within your native + app, you must change the `baseURL` in the server configuration for + `PasswordAuthProvider` to a scheme registered for your app, extract the token + from the URL, and call this method. + + @param serverURL The authentication server URL for the user. + @param token The one-time use token from the URL. + @param completion A block which will be called when the request completes or + fails. The callback will be invoked on a background queue + provided by `NSURLSession`, and not on the calling queue. + */ ++ (void)confirmEmailForAuthServer:(NSURL *)serverURL + token:(NSString *)token + completion:(RLMPasswordChangeStatusBlock)completion; + +#pragma mark - Administrator + +/** + Given a Realm Object Server authentication provider and a provider identifier for a user + (for example, a username), look up and return user information for that user. + + @param providerUserIdentity The username or identity of the user as issued by the authentication provider. + In most cases this is different from the Realm Object Server-issued identity. + @param provider The authentication provider that manages the user whose information is desired. + @param completion Completion block invoked when request has completed or failed. + The callback will be invoked on a background queue provided + by `NSURLSession`. + */ +- (void)retrieveInfoForUser:(NSString *)providerUserIdentity + identityProvider:(RLMIdentityProvider)provider + completion:(RLMRetrieveUserBlock)completion; + +#pragma mark - Permissions + +/** + Asynchronously retrieve all permissions associated with the user calling this method. + + The results will be returned through the callback block, or an error if the operation failed. + The callback block will be run on the same thread the method was called on. + + @warning This method must be called from a thread with a currently active run loop. Unless + you have manually configured a run loop on a side thread, this will usually be the + main thread. + */ +- (void)retrievePermissionsWithCallback:(RLMPermissionResultsBlock)callback NS_REFINED_FOR_SWIFT; + +/** + Apply a given permission. + + The operation will take place asynchronously, and the callback will be used to report whether + the permission change succeeded or failed. The user calling this method must have the right + to grant the given permission, or else the operation will fail. + + @see `RLMSyncPermission` + */ +- (void)applyPermission:(RLMSyncPermission *)permission callback:(RLMPermissionStatusBlock)callback; + /** - Returns an instance of the Management Realm owned by the user. - - This Realm can be used to control access permissions for Realms managed by the user. - This includes granting other users access to Realms. + Revoke a given permission. + + The operation will take place asynchronously, and the callback will be used to report whether + the permission change succeeded or failed. The user calling this method must have the right + to grant the given permission, or else the operation will fail. + + @see `RLMSyncPermission` */ -- (RLMRealm *)managementRealmWithError:(NSError **)error NS_REFINED_FOR_SWIFT; +- (void)revokePermission:(RLMSyncPermission *)permission callback:(RLMPermissionStatusBlock)callback; + +/** + Create a permission offer for a Realm. + + A permission offer is used to grant access to a Realm this user manages to another + user. Creating a permission offer produces a string token which can be passed to the + recepient in any suitable way (for example, via e-mail). + + The operation will take place asynchronously. The token can be accepted by the recepient + using the `-[RLMSyncUser acceptOfferForToken:callback:]` method. + + @param url The URL of the Realm for which the permission offer should pertain. This + may be the URL of any Realm which this user is allowed to manage. If the URL + has a `~` wildcard it will be replaced with this user's user identity. + @param accessLevel What access level to grant to whoever accepts the token. + @param expirationDate Optionally, a date which indicates when the offer expires. If the + recepient attempts to accept the offer after the date it will be rejected. + @param callback A callback indicating whether the operation succeeded or failed. If it + succeeded the token will be passed in as a string. + + @see `acceptOfferForToken:callback:` + */ +- (void)createOfferForRealmAtURL:(NSURL *)url + accessLevel:(RLMSyncAccessLevel)accessLevel + expiration:(nullable NSDate *)expirationDate + callback:(RLMPermissionOfferStatusBlock)callback NS_REFINED_FOR_SWIFT; + +/** + Accept a permission offer. + + Pass in a token representing a permission offer. The operation will take place asynchronously. + If the operation succeeds, the callback will be passed the URL of the Realm for which the + offer applied, so the Realm can be opened. + + The token this method accepts can be created by the offering user through the + `-[RLMSyncUser createOfferForRealmAtURL:accessLevel:expiration:callback:]` method. + + @see `createOfferForRealmAtURL:accessLevel:expiration:callback:` + */ +- (void)acceptOfferForToken:(NSString *)token + callback:(RLMPermissionOfferResponseStatusBlock)callback; /// :nodoc: - (instancetype)init __attribute__((unavailable("RLMSyncUser cannot be created directly"))); - /// :nodoc: + (instancetype)new __attribute__((unavailable("RLMSyncUser cannot be created directly"))); -NS_ASSUME_NONNULL_END +@end + +#pragma mark - User info classes + +/** + A data object representing a user account associated with a user. + + @see `RLMSyncUserInfo` + */ +@interface RLMSyncUserAccountInfo : NSObject + +/// The authentication provider which manages this user account. +@property (nonatomic, readonly) RLMIdentityProvider provider; + +/// The username or identity of this user account. +@property (nonatomic, readonly) NSString *providerUserIdentity; + +/// :nodoc: +- (instancetype)init __attribute__((unavailable("RLMSyncUserAccountInfo cannot be created directly"))); +/// :nodoc: ++ (instancetype)new __attribute__((unavailable("RLMSyncUserAccountInfo cannot be created directly"))); + +@end + +/** + A data object representing information about a user that was retrieved from a user lookup call. + */ +@interface RLMSyncUserInfo : NSObject + +/** + An array of all the user accounts associated with this user. + */ +@property (nonatomic, readonly) NSArray *accounts; + +/** + The identity issued to this user by the Realm Object Server. + */ +@property (nonatomic, readonly) NSString *identity; + +/** + Metadata about this user stored on the Realm Object Server. + */ +@property (nonatomic, readonly) NSDictionary *metadata; + +/** + Whether the user is flagged on the Realm Object Server as an administrator. + */ +@property (nonatomic, readonly) BOOL isAdmin; + +/// :nodoc: +- (instancetype)init __attribute__((unavailable("RLMSyncUserInfo cannot be created directly"))); +/// :nodoc: ++ (instancetype)new __attribute__((unavailable("RLMSyncUserInfo cannot be created directly"))); @end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMSyncUser_Private.hpp b/Pods/Realm/include/RLMSyncUser_Private.hpp index 6d9d0da..8b41d71 100644 --- a/Pods/Realm/include/RLMSyncUser_Private.hpp +++ b/Pods/Realm/include/RLMSyncUser_Private.hpp @@ -21,10 +21,11 @@ #import "RLMSyncConfiguration.h" #import "RLMSyncUtil_Private.h" -#include "sync/sync_config.hpp" -#include "sync/impl/sync_metadata.hpp" +#import "sync/sync_config.hpp" +#import "sync/sync_user.hpp" +#import "sync/impl/sync_metadata.hpp" -@class RLMSyncConfiguration; +@class RLMSyncConfiguration, RLMSyncSessionRefreshHandle; using namespace realm; @@ -32,18 +33,44 @@ typedef void(^RLMFetchedRealmCompletionBlock)(NSError * _Nullable, RLMRealm * _N NS_ASSUME_NONNULL_BEGIN -@interface RLMSyncUser () +class CocoaSyncUserContext : public SyncUserContext { +public: + void register_refresh_handle(const std::string& path, RLMSyncSessionRefreshHandle *handle); + void unregister_refresh_handle(const std::string& path); + void invalidate_all_handles(); + + RLMUserErrorReportingBlock error_handler() const; + void set_error_handler(RLMUserErrorReportingBlock); + +private: + /** + A map of paths to 'refresh handles'. + + A refresh handle is an object that encapsulates the concept of periodically + refreshing the Realm's access token before it expires. Tokens are indexed by their + paths (e.g. `/~/path/to/realm`). + */ + std::unordered_map m_refresh_handles; + std::mutex m_mutex; -- (void)_bindSessionWithConfig:(const SyncConfig&)config - session:(std::shared_ptr)session - completion:(RLMSyncBasicErrorReportingBlock)completion; + /** + An optional callback invoked when the authentication server reports the user as + being in an expired state. + */ + RLMUserErrorReportingBlock m_error_handler; + mutable std::mutex m_error_handler_mutex; +}; +@interface RLMSyncUser () - (instancetype)initWithSyncUser:(std::shared_ptr)user; +- (NSURL *)defaultRealmURL; - (std::shared_ptr)_syncUser; - (nullable NSString *)_refreshToken; ++ (void)_setUpBindingContextFactory; +@end -- (void)_unregisterRefreshHandleForURLPath:(NSString *)path; +using PermissionChangeCallback = std::function; -@end +PermissionChangeCallback RLMWrapPermissionStatusCallback(RLMPermissionStatusBlock callback); NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMSyncUtil.h b/Pods/Realm/include/RLMSyncUtil.h index 71cf8e2..5867d74 100644 --- a/Pods/Realm/include/RLMSyncUtil.h +++ b/Pods/Realm/include/RLMSyncUtil.h @@ -26,36 +26,33 @@ NS_ASSUME_NONNULL_BEGIN /// A user info key for use with `RLMSyncErrorClientResetError`. extern NSString *const kRLMSyncPathOfRealmBackupCopyKey; -/// A user info key for use with `RLMSyncErrorClientResetError`. -extern NSString *const kRLMSyncInitiateClientResetBlockKey; - -/// The error domain string for all SDK errors related to synchronization functionality. +/// A user info key for use with certain error types. +extern NSString *const kRLMSyncErrorActionTokenKey; + +/** + The error domain string for all SDK errors related to errors reported + by the synchronization manager error handler, as well as general sync + errors that don't fall into any of the other categories. + */ extern NSString *const RLMSyncErrorDomain; -/// An error which is related to authentication to a Realm Object Server. -typedef RLM_ERROR_ENUM(NSInteger, RLMSyncAuthError, RLMSyncErrorDomain) { - /// An error that indicates that the provided credentials are invalid. - RLMSyncAuthErrorInvalidCredential = 611, - - /// An error that indicates that the user with provided credentials does not exist. - RLMSyncAuthErrorUserDoesNotExist = 612, - - /// An error that indicates that the user cannot be registered as it exists already. - RLMSyncAuthErrorUserAlreadyExists = 613, -}; - -/// An error which is related to synchronization with a Realm Object Server. +/** + The error domain string for all SDK errors related to the authentication + endpoint. + */ +extern NSString *const RLMSyncAuthErrorDomain; + +/** + The error domain string for all SDK errors related to the permissions + system and APIs. + */ +extern NSString *const RLMSyncPermissionErrorDomain; + +/** + An error related to a problem that might be reported by the synchronization manager + error handler, or a callback on a sync-related API that performs asynchronous work. + */ typedef RLM_ERROR_ENUM(NSInteger, RLMSyncError, RLMSyncErrorDomain) { - /// An error that indicates that the response received from the authentication server was malformed. - RLMSyncErrorBadResponse = 1, - - /// An error that indicates that the supplied Realm path was invalid, or could not be resolved by the authentication - /// server. - RLMSyncErrorBadRemoteRealmPath = 2, - - /// An error that indicates that the response received from the authentication server was an HTTP error code. The - /// `userInfo` dictionary contains the actual error code value. - RLMSyncErrorHTTPStatusCodeError = 3, /// An error that indicates a problem with the session (a specific Realm opened for sync). RLMSyncErrorClientSessionError = 4, @@ -63,7 +60,10 @@ typedef RLM_ERROR_ENUM(NSInteger, RLMSyncError, RLMSyncErrorDomain) { /// An error that indicates a problem with a specific user. RLMSyncErrorClientUserError = 5, - /// An error that indicates an internal, unrecoverable error with the underlying synchronization engine. + /** + An error that indicates an internal, unrecoverable problem + with the underlying synchronization engine. + */ RLMSyncErrorClientInternalError = 6, /** @@ -84,39 +84,139 @@ typedef RLM_ERROR_ENUM(NSInteger, RLMSyncError, RLMSyncErrorDomain) { re-downloaded Realm will initially contain only the data present at the time the Realm was backed up on the server. - The client reset process can be initiated in one of two ways. The block provided in the - `userInfo` dictionary under `kRLMSyncInitiateClientResetBlockKey` can be called to - initiate the reset process. This block can be called any time after the error is - received, but should only be called if and when your app closes and invalidates every + The client reset process can be initiated in one of two ways. + + The `userInfo` dictionary contains an opaque token object under the key + `kRLMSyncErrorActionTokenKey`. This token can be passed into + `+[RLMSyncSession immediatelyHandleError:]` in order to immediately perform the client + reset process. This should only be done after your app closes and invalidates every instance of the offending Realm on all threads (note that autorelease pools may make this difficult to guarantee). - If the block is not called, the client reset process will be automatically carried out - the next time the app is launched and the `RLMSyncManager` singleton is accessed. + If `+[RLMSyncSession immediatelyHandleError:]` is not called, the client reset process + will be automatically carried out the next time the app is launched and the + `RLMSyncManager` singleton is accessed. The value for the `kRLMSyncPathOfRealmBackupCopyKey` key in the `userInfo` dictionary describes the path of the recovered copy of the Realm. This copy will not actually be created until the client reset process is initiated. - @see: `-[NSError rlmSync_clientResetBlock]`, `-[NSError rlmSync_clientResetBackedUpRealmPath]` + @see `-[NSError rlmSync_errorActionToken]`, `-[NSError rlmSync_clientResetBackedUpRealmPath]` */ RLMSyncErrorClientResetError = 7, + + /** + An error that indicates an authentication error occurred. + + The `kRLMSyncUnderlyingErrorKey` key in the user info dictionary will contain the + underlying error, which is guaranteed to be under the `RLMSyncAuthErrorDomain` + error domain. + */ + RLMSyncErrorUnderlyingAuthError = 8, + + /** + An error that indicates the user does not have permission to perform an operation + upon a synced Realm. For example, a user may receive this error if they attempt to + open a Realm they do not have at least read access to, or write to a Realm they only + have read access to. + + This error may also occur if a user incorrectly opens a Realm they have read-only + permissions to without using the `asyncOpen()` APIs. + + A Realm that suffers a permission denied error is, by default, flagged so that its + local copy will be deleted the next time the application starts. + + The `userInfo` dictionary contains an opaque token object under the key + `kRLMSyncErrorActionTokenKey`. This token can be passed into + `+[RLMSyncSession immediatelyHandleError:]` in order to immediately delete the local + copy. This should only be done after your app closes and invalidates every instance + of the offending Realm on all threads (note that autorelease pools may make this + difficult to guarantee). + + @warning It is strongly recommended that, if a Realm has encountered a permission denied + error, its files be deleted before attempting to re-open it. + + @see `-[NSError rlmSync_errorActionToken]` + */ + RLMSyncErrorPermissionDeniedError = 9, }; -/// An enum representing the different states a sync management object can take. -typedef NS_ENUM(NSUInteger, RLMSyncManagementObjectStatus) { - /// The management object has not yet been processed by the object server. - RLMSyncManagementObjectStatusNotProcessed, - /// The operations encoded in the management object have been successfully - /// performed by the object server. - RLMSyncManagementObjectStatusSuccess, +/// An error which is related to authentication to a Realm Object Server. +typedef RLM_ERROR_ENUM(NSInteger, RLMSyncAuthError, RLMSyncAuthErrorDomain) { + /// An error that indicates that the response received from the authentication server was malformed. + RLMSyncAuthErrorBadResponse = 1, + + /// An error that indicates that the supplied Realm path was invalid, or could not be resolved by the authentication + /// server. + RLMSyncAuthErrorBadRemoteRealmPath = 2, + + /// An error that indicates that the response received from the authentication server was an HTTP error code. The + /// `userInfo` dictionary contains the actual error code value. + RLMSyncAuthErrorHTTPStatusCodeError = 3, + + /// An error that indicates a problem with the session (a specific Realm opened for sync). + RLMSyncAuthErrorClientSessionError = 4, + + /// An error that indicates that the provided credentials are ill-formed. + RLMSyncAuthErrorInvalidParameters = 601, + + /// An error that indicates that no Realm path was included in the URL. + RLMSyncAuthErrorMissingPath = 602, + + /// An error that indicates that the provided credentials are invalid. + RLMSyncAuthErrorInvalidCredential = 611, + + /// An error that indicates that the user with provided credentials does not exist. + RLMSyncAuthErrorUserDoesNotExist = 612, + + /// An error that indicates that the user cannot be registered as it exists already. + RLMSyncAuthErrorUserAlreadyExists = 613, + + /// An error that indicates the path is invalid or the user doesn't have access to that Realm. + RLMSyncAuthErrorAccessDeniedOrInvalidPath = 614, + + /// An error that indicates the refresh token was invalid. + RLMSyncAuthErrorInvalidAccessToken = 615, + + /// An error that indicates the permission offer is expired. + RLMSyncAuthErrorExpiredPermissionOffer = 701, + + /// An error that indicates the permission offer is ambiguous. + RLMSyncAuthErrorAmbiguousPermissionOffer = 702, + + /// An error that indicates the file at the given path can't be shared. + RLMSyncAuthErrorFileCannotBeShared = 703, +}; + +/** + An error related to the permissions subsystem. + */ +typedef RLM_ERROR_ENUM(NSInteger, RLMSyncPermissionError, RLMSyncPermissionErrorDomain) { + /** + An error that indicates a permission change operation failed. The `userInfo` + dictionary contains the underlying error code and a message (if any). + */ + RLMSyncPermissionErrorChangeFailed = 1, + + /** + An error that indicates that attempting to retrieve permissions failed. + */ + RLMSyncPermissionErrorGetFailed = 2, + + /** + An error that indicates that trying to create a permission offer failed. + */ + RLMSyncPermissionErrorOfferFailed = 3, + + /** + An error that indicates that trying to accept a permission offer failed. + */ + RLMSyncPermissionErrorAcceptOfferFailed = 4, + /** - The operations encoded in the management object were not successfully - performed by the object server. - Refer to the `statusCode` and `statusMessage` properties for more details - about the error. + An error that indicates that an internal error occurred. */ - RLMSyncManagementObjectStatusError, + RLMSyncPermissionErrorInternal = 5, }; NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMSyncUtil_Private.h b/Pods/Realm/include/RLMSyncUtil_Private.h index e3ac5ca..d4c044f 100644 --- a/Pods/Realm/include/RLMSyncUtil_Private.h +++ b/Pods/Realm/include/RLMSyncUtil_Private.h @@ -22,6 +22,18 @@ #import #import +typedef NS_ENUM(NSUInteger, RLMSyncSystemErrorKind) { + // Specific + RLMSyncSystemErrorKindClientReset, + RLMSyncSystemErrorKindPermissionDenied, + // General + RLMSyncSystemErrorKindClient, + RLMSyncSystemErrorKindConnection, + RLMSyncSystemErrorKindSession, + RLMSyncSystemErrorKindUser, + RLMSyncSystemErrorKindUnknown, +}; + @class RLMSyncUser; typedef void(^RLMSyncCompletionBlock)(NSError * _Nullable, NSDictionary * _Nullable); @@ -31,10 +43,6 @@ typedef NSString* RLMServerPath; NS_ASSUME_NONNULL_BEGIN -@interface RLMRealmConfiguration (RealmSync) -+ (instancetype)managementConfigurationForUser:(RLMSyncUser *)user; -@end - extern RLMIdentityProvider const RLMIdentityProviderAccessToken; extern RLMIdentityProvider const RLMIdentityProviderRealm; @@ -43,15 +51,18 @@ extern NSString *const kRLMSyncDataKey; extern NSString *const kRLMSyncErrorJSONKey; extern NSString *const kRLMSyncErrorStatusCodeKey; extern NSString *const kRLMSyncIdentityKey; +extern NSString *const kRLMSyncIsAdminKey; +extern NSString *const kRLMSyncNewPasswordKey; extern NSString *const kRLMSyncPasswordKey; extern NSString *const kRLMSyncPathKey; +extern NSString *const kRLMSyncTokenKey; extern NSString *const kRLMSyncProviderKey; +extern NSString *const kRLMSyncProviderIDKey; extern NSString *const kRLMSyncRegisterKey; extern NSString *const kRLMSyncUnderlyingErrorKey; +extern NSString *const kRLMSyncUserIDKey; -/// Convert sync management object status code (nil, 0 and others) to -/// RLMSyncManagementObjectStatus enum -FOUNDATION_EXTERN RLMSyncManagementObjectStatus RLMMakeSyncManagementObjectStatus(NSNumber * _Nullable statusCode); +FOUNDATION_EXTERN uint8_t RLMGetComputedPermissions(RLMRealm *realm, id _Nullable object); #define RLM_SYNC_UNINITIALIZABLE \ - (instancetype)init __attribute__((unavailable("This type cannot be created directly"))); \ @@ -74,6 +85,13 @@ if (![data isKindOfClass:[NSString class]]) { data = nil; } \ self.prop_macro_val = data; \ } \ +#define RLM_SYNC_PARSE_OPTIONAL_BOOL(json_macro_val, key_macro_val, prop_macro_val) \ +{ \ +id data = json_macro_val[key_macro_val]; \ +if (![data isKindOfClass:[NSNumber class]]) { data = @NO; } \ +self.prop_macro_val = [data boolValue]; \ +} \ + /// A macro to parse a double out of a JSON dictionary, or return nil. #define RLM_SYNC_PARSE_DOUBLE_OR_ABORT(json_macro_val, key_macro_val, prop_macro_val) \ { \ @@ -92,6 +110,21 @@ if (!model) { return nil; } \ self.prop_macro_val = model; \ } \ +/// A macro to build an array of sub-models out of a JSON dictionary, or return nil. +#define RLM_SYNC_PARSE_MODEL_ARRAY_OR_ABORT(json_macro_val, key_macro_val, class_macro_val, prop_macro_val) \ +{ \ +NSArray *jsonArray = json_macro_val[key_macro_val]; \ +if (![jsonArray isKindOfClass:[NSArray class]]) { return nil; } \ +NSMutableArray *buffer = [NSMutableArray array]; \ +for (id value in jsonArray) { \ +id next = nil; \ +if ([value isKindOfClass:[NSDictionary class]]) { next = [[class_macro_val alloc] initWithDictionary:value]; } \ +if (!next) { return nil; } \ +[buffer addObject:next]; \ +} \ +self.prop_macro_val = [buffer copy]; \ +} \ + #define RLM_SYNC_PARSE_OPTIONAL_MODEL(json_macro_val, key_macro_val, class_macro_val, prop_macro_val) \ { \ id model; \ diff --git a/Pods/Realm/include/RLMSyncUtil_Private.hpp b/Pods/Realm/include/RLMSyncUtil_Private.hpp index 279207a..3ea2130 100644 --- a/Pods/Realm/include/RLMSyncUtil_Private.hpp +++ b/Pods/Realm/include/RLMSyncUtil_Private.hpp @@ -19,12 +19,56 @@ #import "RLMSyncUtil_Private.h" #import "RLMSyncConfiguration_Private.h" +#import "RLMSyncPermission.h" #import "sync/sync_manager.hpp" +#import "realm/util/optional.hpp" + +@class RLMSyncErrorResponseModel; +class CocoaSyncUserContext; namespace realm { +enum class AccessLevel; +} -SyncSessionStopPolicy translateStopPolicy(RLMSyncStopPolicy stopPolicy); -RLMSyncStopPolicy translateStopPolicy(SyncSessionStopPolicy stop_policy); +realm::SyncSessionStopPolicy translateStopPolicy(RLMSyncStopPolicy stopPolicy); +RLMSyncStopPolicy translateStopPolicy(realm::SyncSessionStopPolicy stop_policy); -} +std::shared_ptr sync_session_for_realm(RLMRealm *realm); + +#pragma mark - Get user context + +CocoaSyncUserContext& context_for(const std::shared_ptr& user); + +#pragma mark - Access level conversion + +realm::AccessLevel accessLevelForObjCAccessLevel(RLMSyncAccessLevel level); +RLMSyncAccessLevel objCAccessLevelForAccessLevel(realm::AccessLevel level); + +#pragma mark - Error conversion + +typedef enum : NSUInteger { + RLMPermissionActionTypeGet, + RLMPermissionActionTypeChange, + RLMPermissionActionTypeOffer, + RLMPermissionActionTypeAcceptOffer, +} RLMPermissionActionType; + +NSError *translateSyncExceptionPtrToError(std::exception_ptr ptr, RLMPermissionActionType type); + +#pragma mark - Error construction + +NSError *make_auth_error_bad_response(NSDictionary *json=nil); +NSError *make_auth_error_http_status(NSInteger status); +NSError *make_auth_error_client_issue(); +NSError *make_auth_error(RLMSyncErrorResponseModel *responseModel); + +NSError *make_permission_error_get(NSString *description, realm::util::Optional code=none); +NSError *make_permission_error_change(NSString *description, realm::util::Optional code=none); +NSError *make_permission_error_offer(NSString *description, realm::util::Optional code=none); +NSError *make_permission_error_accept_offer(NSString *description, realm::util::Optional code=none); + +// Set 'code' to NSNotFound to not actually have an error code. +NSError *make_sync_error(RLMSyncSystemErrorKind kind, NSString *description, NSInteger code, NSDictionary *custom); +NSError *make_sync_error(NSError *wrapped_auth_error); +NSError *make_sync_error(std::error_code, RLMSyncSystemErrorKind kind=RLMSyncSystemErrorKindSession); diff --git a/Pods/Realm/include/RLMTokenModels.h b/Pods/Realm/include/RLMTokenModels.h deleted file mode 100644 index bd97092..0000000 --- a/Pods/Realm/include/RLMTokenModels.h +++ /dev/null @@ -1,49 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#import - -#import "RLMSyncUtil_Private.h" - -NS_ASSUME_NONNULL_BEGIN - -@class RLMTokenDataModel; - -@interface RLMTokenModel : NSObject RLM_SYNC_UNINITIALIZABLE - -@property (nonatomic, readonly) NSString *token; -@property (nonatomic, nullable, readonly) NSString *path; -@property (nonatomic, readonly) RLMTokenDataModel *tokenData; - -- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary; - -@end - -@interface RLMTokenDataModel : NSObject RLM_SYNC_UNINITIALIZABLE - -@property (nonatomic, readonly) NSString *identity; -@property (nonatomic, nullable, readonly) NSString *appID; -@property (nonatomic, nullable, readonly) NSString *path; -@property (nonatomic, readonly) NSTimeInterval expires; -//@property (nonatomic, readonly) NSArray *access; - -- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMUtil.hpp b/Pods/Realm/include/RLMUtil.hpp index e0161b9..f567469 100644 --- a/Pods/Realm/include/RLMUtil.hpp +++ b/Pods/Realm/include/RLMUtil.hpp @@ -45,12 +45,18 @@ NSError *RLMMakeError(RLMError code, std::exception const& exception); NSError *RLMMakeError(RLMError code, const realm::util::File::AccessError&); NSError *RLMMakeError(RLMError code, const realm::RealmFileException&); NSError *RLMMakeError(std::system_error const& exception); -NSError *RLMMakeError(NSException *exception); void RLMSetErrorOrThrow(NSError *error, NSError **outError); // returns if the object can be inserted as the given type BOOL RLMIsObjectValidForProperty(id obj, RLMProperty *prop); +// throw an exception if the object is not a valid value for the property +void RLMValidateValueForProperty(id obj, RLMObjectSchema *objectSchema, + RLMProperty *prop, bool validateObjects=false); +BOOL RLMValidateValue(id value, RLMPropertyType type, bool optional, bool array, + NSString *objectClassName); + +void RLMThrowTypeError(id obj, RLMObjectSchema *objectSchema, RLMProperty *prop); // gets default values for the given schema (+defaultPropertyValues) // merges with native property defaults if Swift class @@ -68,12 +74,6 @@ static inline BOOL RLMIsKindOfClass(Class class1, Class class2) { return NO; } -// Returns whether the class is a descendent of RLMObjectBase -BOOL RLMIsObjectOrSubclass(Class klass); - -// Returns whether the class is an indirect descendant of RLMObjectBase -BOOL RLMIsObjectSubclass(Class klass); - template static inline T *RLMDynamicCast(__unsafe_unretained id obj) { if ([obj isKindOfClass:[T class]]) { @@ -82,44 +82,19 @@ static inline T *RLMDynamicCast(__unsafe_unretained id obj) { return nil; } -template -static inline T RLMCoerceToNil(__unsafe_unretained T obj) { +static inline id RLMCoerceToNil(__unsafe_unretained id obj) { if (static_cast(obj) == NSNull.null) { return nil; } else if (__unsafe_unretained auto optional = RLMDynamicCast(obj)) { - return RLMCoerceToNil(optional.underlyingValue); + return RLMCoerceToNil(RLMGetOptional(optional)); } return obj; } -// Translate an rlmtype to a string representation -static inline NSString *RLMTypeToString(RLMPropertyType type) { - switch (type) { - case RLMPropertyTypeString: - return @"string"; - case RLMPropertyTypeInt: - return @"int"; - case RLMPropertyTypeBool: - return @"bool"; - case RLMPropertyTypeDate: - return @"date"; - case RLMPropertyTypeData: - return @"data"; - case RLMPropertyTypeDouble: - return @"double"; - case RLMPropertyTypeFloat: - return @"float"; - case RLMPropertyTypeAny: - return @"any"; - case RLMPropertyTypeObject: - return @"object"; - case RLMPropertyTypeArray: - return @"array"; - case RLMPropertyTypeLinkingObjects: - return @"linking objects"; - } - return @"Unknown"; +template +static inline T RLMCoerceToNil(__unsafe_unretained T obj) { + return RLMCoerceToNil(static_cast(obj)); } // String conversion utilities @@ -140,7 +115,7 @@ static inline realm::StringData RLMStringDataWithNSString(__unsafe_unretained NS static_assert(sizeof(size_t) >= sizeof(NSUInteger), "Need runtime overflow check for NSUInteger to size_t conversion"); return realm::StringData(string.UTF8String, - [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); + [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); } // Binary conversion utilities @@ -167,6 +142,8 @@ static inline NSDate *RLMTimestampToNSDate(realm::Timestamp ts) NS_RETURNS_RETAI } static inline realm::Timestamp RLMTimestampForNSDate(__unsafe_unretained NSDate *const date) { + if (!date) + return {}; auto timeInterval = date.timeIntervalSinceReferenceDate; if (isnan(timeInterval)) return {0, 0}; // Arbitrary choice @@ -195,10 +172,6 @@ static inline NSUInteger RLMConvertNotFound(size_t index) { id RLMMixedToObjc(realm::Mixed const& value); -// For unit testing purposes, allow an Objective-C class named FakeObject to also be used -// as the base class of managed objects. This allows for testing invalid schemas. -void RLMSetTreatFakeObjectAsRLMObject(BOOL flag); - // Given a bundle identifier, return the base directory on the disk within which Realm database and support files should // be stored. NSString *RLMDefaultDirectoryForBundleIdentifier(NSString *bundleIdentifier); diff --git a/Pods/Realm/include/Realm.h b/Pods/Realm/include/Realm.h index 4514a95..f460336 100644 --- a/Pods/Realm/include/Realm.h +++ b/Pods/Realm/include/Realm.h @@ -25,6 +25,7 @@ #import #import #import +#import #import #import #import @@ -32,10 +33,9 @@ #import #import #import -#import -#import -#import +#import #import +#import #import #import #import diff --git a/Pods/Realm/include/binding_callback_thread_observer.hpp b/Pods/Realm/include/binding_callback_thread_observer.hpp new file mode 100644 index 0000000..e956349 --- /dev/null +++ b/Pods/Realm/include/binding_callback_thread_observer.hpp @@ -0,0 +1,42 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2017 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#ifndef REALM_OS_BINDING_CALLBACK_THREAD_OBSERVER_HPP +#define REALM_OS_BINDING_CALLBACK_THREAD_OBSERVER_HPP + +#include + +namespace realm { +// Interface for bindings interested in registering callbacks before/after the ObjectStore thread runs. +// This is for example helpful to attach/detach the pthread to the JavaVM in order to be able to perform JNI calls. +class BindingCallbackThreadObserver { +public: + // This method is called just before the thread is started + virtual void did_create_thread() = 0; + + // This method is called just before the thread is being destroyed + virtual void will_destroy_thread() = 0; + + // This method is called with any exception throws by client.run(). + virtual void handle_error(std::exception const& e) = 0; +}; + +extern BindingCallbackThreadObserver* g_binding_callback_thread_observer; +} + +#endif // REALM_OS_BINDING_CALLBACK_THREAD_OBSERVER_HPP diff --git a/Pods/Realm/include/binding_context.hpp b/Pods/Realm/include/binding_context.hpp index 5fc59eb..8d7532c 100644 --- a/Pods/Realm/include/binding_context.hpp +++ b/Pods/Realm/include/binding_context.hpp @@ -27,7 +27,7 @@ namespace realm { // BindingContext is the extension point for adding binding-specific behavior to -// a SharedRealm. It can be used to store additonal data associated with the +// a SharedRealm. It can be used to store additional data associated with the // Realm which is needed by the binding, and there are several methods which // can be overridden to receive notifications of state changes within the Realm. // @@ -68,6 +68,7 @@ namespace realm { // std::list> m_registered_notifications; // }; class Realm; +class Schema; class BindingContext { public: virtual ~BindingContext() = default; @@ -78,6 +79,15 @@ class BindingContext { // actually be called? virtual bool can_deliver_notifications() const noexcept { return true; } + // Called when the Realm is about to send notifications about Realm, + // Collection or Object changes. This method will be called even if + // no notification callbacks have been registered. + virtual void will_send_notifications() { } + + // Called when the Realm is done sending all change notifications. This method + // will be called even if no notification callbacks have been registered. + virtual void did_send_notifications() { } + // Called by the Realm when refresh called or a notification arrives which // is triggered through write transaction committed by itself or a different // Realm instance. @@ -114,6 +124,12 @@ class BindingContext { std::vector const& invalidated, bool version_changed=true); + // Called immediately after the corresponding Realm's schema is changed through + // update_schema()/set_schema_subset() or the schema is changed by another Realm + // instance. The parameter is a schema reference which is the same as the return + // value of Realm::schema(). + virtual void schema_did_change(Schema const&) {} + // Change information for a single field of a row struct ColumnInfo { // The index of this column prior to the changes in the tracked diff --git a/Pods/Realm/include/collection_notifications.hpp b/Pods/Realm/include/collection_notifications.hpp index c1be7be..919e8b4 100644 --- a/Pods/Realm/include/collection_notifications.hpp +++ b/Pods/Realm/include/collection_notifications.hpp @@ -35,7 +35,7 @@ namespace _impl { // A token which keeps an asynchronous query alive struct NotificationToken { NotificationToken() = default; - NotificationToken(std::shared_ptr<_impl::CollectionNotifier> notifier, size_t token); + NotificationToken(std::shared_ptr<_impl::CollectionNotifier> notifier, uint64_t token); ~NotificationToken(); NotificationToken(NotificationToken&&); @@ -48,7 +48,7 @@ struct NotificationToken { private: util::AtomicSharedPtr<_impl::CollectionNotifier> m_notifier; - size_t m_token; + uint64_t m_token; }; struct CollectionChangeSet { @@ -56,7 +56,7 @@ struct CollectionChangeSet { size_t from; size_t to; - bool operator==(Move m) const { return from == m.from && to == m.to; } + bool operator==(Move m) const noexcept { return from == m.from && to == m.to; } }; // Indices which were removed from the _old_ collection @@ -88,7 +88,7 @@ struct CollectionChangeSet { // Per-column version of `modifications` std::vector columns; - bool empty() const + bool empty() const noexcept { return deletions.empty() && insertions.empty() && modifications.empty() && modifications_new.empty() && moves.empty(); diff --git a/Pods/Realm/include/core/realm.hpp b/Pods/Realm/include/core/realm.hpp index 29e4844..7ede53f 100644 --- a/Pods/Realm/include/core/realm.hpp +++ b/Pods/Realm/include/core/realm.hpp @@ -20,8 +20,11 @@ #define REALM_HPP #include -#include #include #include +#include +#include +#include +#include #endif // REALM_HPP diff --git a/Pods/Realm/include/core/realm/alloc.hpp b/Pods/Realm/include/core/realm/alloc.hpp index 86eb04a..157d244 100644 --- a/Pods/Realm/include/core/realm/alloc.hpp +++ b/Pods/Realm/include/core/realm/alloc.hpp @@ -1,4 +1,4 @@ -/************************************************************************* +/************************************************************************* * * Copyright 2016 Realm Inc. * @@ -26,7 +26,6 @@ #include #include #include -#include namespace realm { @@ -80,8 +79,6 @@ class MemRef { /// \sa SlabAlloc class Allocator { public: - static constexpr int CURRENT_FILE_FORMAT_VERSION = 6; - /// The specified size must be divisible by 8, and must not be /// zero. /// @@ -121,6 +118,10 @@ class Allocator { virtual ~Allocator() noexcept; + // Disable copying. Copying an allocator can produce double frees. + Allocator(const Allocator&) = delete; + Allocator& operator=(const Allocator&) = delete; + virtual void verify() const = 0; #ifdef REALM_DEBUG @@ -137,86 +138,11 @@ class Allocator { Replication* get_replication() noexcept; - /// \brief The version of the format of the the node structure (in file or - /// in memory) in use by Realm objects associated with this allocator. - /// - /// Every allocator contains a file format version field, which is returned - /// by this function. In some cases (as mentioned below) the file format can - /// change. - /// - /// A value of zero means the the file format is not yet decided. This is - /// only possible for empty Realms where top-ref is zero. - /// - /// For the default allocator (get_default()), the file format version field - /// can never change, is never zero, and is set to whatever - /// Group::get_target_file_format_version_for_session() would return if the - /// original file format version was undecided and the request history type - /// was Replication::hist_None. - /// - /// For the slab allocator (AllocSlab), the file format version field is set - /// to the file format version specified by the attached file (or attached - /// memory buffer) at the time of attachment. If no file (or buffer) is - /// currently attached, the returned value has no meaning. If the Realm file - /// format is later upgraded, the file format version filed must be updated - /// to reflect that fact. - /// - /// In shared mode (when a Realm file is opened via a SharedGroup instance) - /// it can happen that the file format is upgraded asyncronously (via - /// another SharedGroup instance), and in that case the file format version - /// field of the allocator can get out of date, but only for a short - /// while. It is always garanteed to be, and remain up to date after the - /// opening process completes (when SharedGroup::do_open() returns). - /// - /// An empty Realm file (one whose top-ref is zero) may specify a file - /// format version of zero to indicate that the format is not yet - /// decided. In that case, this function will return zero immediately after - /// AllocSlab::attach_file() returns. It shall be guaranteed, however, that - /// the zero is changed to a proper file format version before the opening - /// process completes (Group::open() or SharedGroup::open()). It is the duty - /// of the caller of AllocSlab::attach_file() to ensure this. - /// - /// File format versions: - /// - /// 1 Initial file format version - /// - /// 2 Various changes. - /// - /// 3 Supporting null on string columns broke the file format in following - /// way: Index appends an 'X' character to all strings except the null - /// string, to be able to distinguish between null and empty - /// string. Bumped to 3 because of null support of String columns and - /// because of new format of index. - /// - /// 4 Introduction of optional in-Realm history of changes (additional - /// entries in Group::m_top). Since this change is not forward - /// compatible, the file format version had to be bumped. This change is - /// implemented in a way that achieves backwards compatibility with - /// version 3 (and in turn with version 2). - /// - /// 5 Introduced the new Timestamp column type that replaces DateTime. - /// When opening an older database file, all DateTime columns will be - /// automatically upgraded Timestamp columns. - /// - /// 6 Introduced a new structure for the StringIndex. Moved the commit - /// logs into the Realm file. Changes to the transaction log format - /// including reshuffling instructions. This is the format used in - /// milestone 2.0.0. - /// - /// IMPORTANT: When introducing a new file format version, be sure to review - /// the file validity checks in AllocSlab::validate_buffer(), the file - /// format selection logic in - /// Group::get_target_file_format_version_for_session(), and the file format - /// upgrade logic in Group::upgrade_file_format(). - int get_file_format_version() const noexcept; - protected: size_t m_baseline = 0; // Separation line between immutable and mutable refs. Replication* m_replication = nullptr; - /// See get_file_format_version(). - int m_file_format_version = 0; - ref_type m_debug_watch = 0; /// The specified size must be divisible by 8, and must not be @@ -233,10 +159,10 @@ class Allocator { /// the old chunk. /// /// \throw std::bad_alloc If insufficient memory was available. - virtual MemRef do_realloc(ref_type, const char* addr, size_t old_size, size_t new_size) = 0; + virtual MemRef do_realloc(ref_type, char* addr, size_t old_size, size_t new_size) = 0; /// Release the specified chunk of memory. - virtual void do_free(ref_type, const char* addr) noexcept = 0; + virtual void do_free(ref_type, char* addr) noexcept = 0; /// Map the specified \a ref to the corresponding memory /// address. Note that if is_read_only(ref) returns true, then the @@ -257,25 +183,36 @@ class Allocator { // that can make sync_if_needed() re-run queries even though it is not required. // It must be atomic because it's shared. std::atomic m_table_versioning_counter; + std::atomic m_latest_observed_counter; /// Bump the global version counter. This method should be called when /// version bumping is initiated. Then following calls to should_propagate_version() /// can be used to prune the version bumping. - uint_fast64_t bump_global_version() noexcept; + void bump_global_version() noexcept; /// Determine if the "local_version" is out of sync, so that it should /// be updated. In that case: also update it. Called from Table::bump_version /// to control propagation of version updates on tables within the group. bool should_propagate_version(uint_fast64_t& local_version) noexcept; + /// Note the current global version has been observed. + void observe_version() noexcept; + friend class Table; friend class Group; }; -inline uint_fast64_t Allocator::bump_global_version() noexcept +inline void Allocator::bump_global_version() noexcept { - ++m_table_versioning_counter; - return m_table_versioning_counter; + if (m_latest_observed_counter == m_table_versioning_counter) + m_table_versioning_counter += 1; +} + + +inline void Allocator::observe_version() noexcept +{ + if (m_latest_observed_counter != m_table_versioning_counter) + m_latest_observed_counter.store(m_table_versioning_counter, std::memory_order_relaxed); } @@ -312,9 +249,9 @@ inline ref_type to_ref(int_fast64_t v) noexcept // C++11 standard, paragraph 4.7.2 [conv.integral]: // If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source - // integer (modulo 2n where n is the number of bits used to represent the unsigned type). [ Note: In a two’s + // integer (modulo 2n where n is the number of bits used to represent the unsigned type). [ Note: In a two's // complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is - // no truncation). — end note ] + // no truncation). - end note ] static_assert(std::is_unsigned::value, "If ref_type changes, from_ref and to_ref should probably be updated"); return ref_type(v); @@ -401,7 +338,7 @@ inline MemRef Allocator::realloc_(ref_type ref, const char* addr, size_t old_siz if (ref == m_debug_watch) REALM_TERMINATE("Allocator watch: Ref was reallocated"); #endif - return do_realloc(ref, addr, old_size, new_size); + return do_realloc(ref, const_cast(addr), old_size, new_size); } inline void Allocator::free_(ref_type ref, const char* addr) noexcept @@ -410,7 +347,7 @@ inline void Allocator::free_(ref_type ref, const char* addr) noexcept if (ref == m_debug_watch) REALM_TERMINATE("Allocator watch: Ref was freed"); #endif - return do_free(ref, addr); + return do_free(ref, const_cast(addr)); } inline void Allocator::free_(MemRef mem) noexcept @@ -433,6 +370,7 @@ inline bool Allocator::is_read_only(ref_type ref) const noexcept inline Allocator::Allocator() noexcept { m_table_versioning_counter = 0; + m_latest_observed_counter = 0; } inline Allocator::~Allocator() noexcept @@ -444,12 +382,6 @@ inline Replication* Allocator::get_replication() noexcept return m_replication; } -inline int Allocator::get_file_format_version() const noexcept -{ - return m_file_format_version; -} - - } // namespace realm #endif // REALM_ALLOC_HPP diff --git a/Pods/Realm/include/core/realm/alloc_slab.hpp b/Pods/Realm/include/core/realm/alloc_slab.hpp index 2aeea87..8024a9b 100644 --- a/Pods/Realm/include/core/realm/alloc_slab.hpp +++ b/Pods/Realm/include/core/realm/alloc_slab.hpp @@ -21,6 +21,7 @@ #include // unint8_t etc #include +#include #include #include @@ -60,6 +61,10 @@ class SlabAlloc : public Allocator { ~SlabAlloc() noexcept override; SlabAlloc(); + // Disable copying. Copying an allocator can produce double frees. + SlabAlloc(const SlabAlloc&) = delete; + SlabAlloc& operator=(const SlabAlloc&) = delete; + /// \struct Config /// \brief Storage for combining setup flags for initialization to /// the SlabAlloc. @@ -122,14 +127,6 @@ class SlabAlloc : public Allocator { /// instance), the caller (SharedGroup::do_open()) must take steps to ensure /// cross-process mutual exclusion. /// - /// If the attached file contains an empty Realm (one whose top-ref is - /// zero), the file format version may remain undecided upon return from - /// this function. The file format is undecided if, and only if - /// get_file_format_version() returns zero. The caller is required to check - /// for this case, and decide on a file format version. This must happen - /// before the Realm opening process completes, and the decided file format - /// must be set in the allocator by calling set_file_format_version(). - /// /// Except for \a file_path, the parameters are passed in through a /// configuration object. /// @@ -153,14 +150,6 @@ class SlabAlloc : public Allocator { /// Attach this allocator to the specified memory buffer. /// - /// If the attached buffer contains an empty Realm (one whose top-ref is - /// zero), the file format version may remain undecided upon return from - /// this function. The file format is undecided if, and only if - /// get_file_format_version() returns zero. The caller is required to check - /// for this case, and decide on a file format version. This must happen - /// before the Realm opening process completes, and the decided file format - /// must be set in the allocator by calling set_file_format_version(). - /// /// It is an error to call this function on an attached /// allocator. Doing so will result in undefined behavor. /// @@ -177,12 +166,6 @@ class SlabAlloc : public Allocator { /// Attach this allocator to an empty buffer. /// - /// Upon return from this function, the file format is undecided - /// (get_file_format_version() returns zero). The caller is required to - /// decide on a file format version. This must happen before the Realm - /// opening process completes, and the decided file format must be set in - /// the allocator by calling set_file_format_version(). - /// /// It is an error to call this function on an attached /// allocator. Doing so will result in undefined behavor. void attach_empty(); @@ -234,6 +217,12 @@ class SlabAlloc : public Allocator { /// attached to a file. Doing so will result in undefined behavior. void resize_file(size_t new_file_size); +#ifdef REALM_DEBUG + /// Deprecated method, only called from a unit test + /// + /// WARNING: This method is NOT thread safe on multiple platforms; see + /// File::prealloc(). + /// /// Reserve disk space now to avoid allocation errors at a later point in /// time, and to minimize on-disk fragmentation. In some cases, less /// fragmentation translates into improved performance. On SSD-drives @@ -245,15 +234,14 @@ class SlabAlloc : public Allocator { /// allocation is done lazily by default). If the file is already bigger /// than the specified size, the size will be unchanged, and on-disk /// allocation will occur only for the initial section that corresponds to - /// the specified size. On systems that do not support preallocation, this - /// function has no effect. To know whether preallocation is supported by - /// Realm on your platform, call util::File::is_prealloc_supported(). + /// the specified size. /// /// This function will call File::sync() if it changes the size of the file. /// /// It is an error to call this function on an allocator that is not /// attached to a file. Doing so will result in undefined behavior. void reserve_disk_space(size_t size_in_bytes); +#endif /// Get the size of the attached database file or buffer in number /// of bytes. This size is not affected by new allocations. After @@ -300,18 +288,6 @@ class SlabAlloc : public Allocator { /// call to SlabAlloc::alloc() corresponds to a mutation event. bool is_free_space_clean() const noexcept; - /// \brief Update the file format version field of the allocator. - /// - /// This must be done during the opening of the Realm if the stored file - /// format version is zero (empty Realm), or after the file format is - /// upgraded. - /// - /// Note that this does not modify the attached file, only the "cached" - /// value subsequenty returned by get_file_format_version(). - /// - /// \sa get_file_format_version() - void set_file_format_version(int) noexcept; - void verify() const override; #ifdef REALM_DEBUG void enable_debug(bool enable) @@ -325,11 +301,41 @@ class SlabAlloc : public Allocator { protected: MemRef do_alloc(const size_t size) override; - MemRef do_realloc(ref_type, const char*, size_t old_size, size_t new_size) override; + MemRef do_realloc(ref_type, char*, size_t old_size, size_t new_size) override; // FIXME: It would be very nice if we could detect an invalid free operation in debug mode - void do_free(ref_type, const char*) noexcept override; + void do_free(ref_type, char*) noexcept override; char* do_translate(ref_type) const noexcept override; + /// Returns the first section boundary *above* the given position. + size_t get_upper_section_boundary(size_t start_pos) const noexcept; + + /// Returns the first section boundary *at or below* the given position. + size_t get_lower_section_boundary(size_t start_pos) const noexcept; + + /// Returns true if the given position is at a section boundary + bool matches_section_boundary(size_t pos) const noexcept; + + /// Returns the index of the section holding a given address. + /// The section index is determined solely by the minimal section size, + /// and does not necessarily reflect the mapping. A mapping may + /// cover multiple sections - the initial mapping often does. + size_t get_section_index(size_t pos) const noexcept; + + /// Reverse: get the base offset of a section at a given index. Since the + /// computation is very time critical, this method just looks it up in + /// a table. The actual computation and setup of that table is done + /// during initialization with the help of compute_section_base() below. + inline size_t get_section_base(size_t index) const noexcept; + + /// Actually compute the starting offset of a section. Only used to initialize + /// a table of predefined results, which are then used by get_section_base(). + size_t compute_section_base(size_t index) const noexcept; + + /// Find a possible allocation of 'request_size' that will fit into a section + /// which is inside the range from 'start_pos' to 'start_pos'+'free_chunk_size' + /// If found return the position, if not return 0. + size_t find_section_in_range(size_t start_pos, size_t free_chunk_size, size_t request_size) const noexcept; + private: void internal_invalidate_cache() noexcept; enum AttachMode { @@ -350,11 +356,104 @@ class SlabAlloc : public Allocator { ref_type ref_end; char* addr; }; - struct Chunk { + struct Chunk { // describes a freed in-file block ref_type ref; size_t size; }; + // free blocks that are in the slab area are managed using the following structures: + // - FreeBlock: Placed at the start of any free space. Holds the 'ref' corresponding to + // the start of the space, and prev/next links used to place it in a size-specific + // freelist. + // - BetweenBlocks: Structure sitting between any two free OR allocated spaces. + // describes the size of the space before and after. + // Each slab (area obtained from the underlying system) has a terminating BetweenBlocks + // at the beginning and at the end of the Slab. + struct FreeBlock { + ref_type ref; // ref for this entry. Saves a reverse translate / representing links as refs + FreeBlock* prev; // circular doubly linked list + FreeBlock* next; + void clear_links() { prev = next = nullptr; } + void unlink(); + }; + struct BetweenBlocks { // stores sizes and used/free status of blocks before and after. + int32_t block_before_size; // negated if block is in use, + int32_t block_after_size; // positive if block is free - and zero at end + }; + + using FreeListMap = std::map; // log(N) addressing for larger blocks + FreeListMap m_block_map; + + // abstract notion of a freelist - used to hide whether a freelist + // is residing in the small blocks or the large blocks structures. + struct FreeList { + int size = 0; // size of every element in the list, 0 if not found + FreeListMap::iterator it; + bool found_something() { return size != 0; } + bool found_exact(int sz) { return size == sz; } + }; + + // simple helper functions for accessing/navigating blocks and betweenblocks (TM) + BetweenBlocks* bb_before(FreeBlock* entry) const { + return reinterpret_cast(entry) - 1; + } + BetweenBlocks* bb_after(FreeBlock* entry) const { + auto bb = bb_before(entry); + size_t sz = bb->block_after_size; + char* addr = reinterpret_cast(entry) + sz; + return reinterpret_cast(addr); + } + FreeBlock* block_before(BetweenBlocks* bb) const { + size_t sz = bb->block_before_size; + if (sz <= 0) return nullptr; // only blocks that are not in use + char* addr = reinterpret_cast(bb) - sz; + return reinterpret_cast(addr); + } + FreeBlock* block_after(BetweenBlocks* bb) const { + if (bb->block_after_size <= 0) + return nullptr; + return reinterpret_cast(bb + 1); + } + int size_from_block(FreeBlock* entry) const { + return bb_before(entry)->block_after_size; + } + void mark_allocated(FreeBlock* entry); + // mark the entry freed in bordering BetweenBlocks. Also validate size. + void mark_freed(FreeBlock* entry, int size); + + // hook for the memory verifier in Group. + template + void for_all_free_entries(Func f) const; + + // Main entry points for alloc/free: + FreeBlock* allocate_block(int size); + void free_block(ref_type ref, FreeBlock* addr); + + // Searching/manipulating freelists + FreeList find(int size); + FreeList find_larger(FreeList hint, int size); + FreeBlock* pop_freelist_entry(FreeList list); + void push_freelist_entry(FreeBlock* entry); + void remove_freelist_entry(FreeBlock* element); + void rebuild_freelists_from_slab(); + void clear_freelists(); + + // grow the slab area to accommodate the requested size. + // returns a free block large enough to handle the request. + FreeBlock* grow_slab_for(int request_size); + // create a single free chunk with "BetweenBlocks" at both ends and a + // single free chunk between them. This free chunk will be of size: + // slab_size - 2 * sizeof(BetweenBlocks) + FreeBlock* slab_to_entry(Slab slab, ref_type ref_start); + + // breaking/merging of blocks + FreeBlock* get_prev_block_if_mergeable(FreeBlock* block); + FreeBlock* get_next_block_if_mergeable(FreeBlock* block); + // break 'block' to give it 'new_size'. Return remaining block. + // If the block is too small to split, return nullptr. + FreeBlock* break_block(FreeBlock* block, int new_size); + FreeBlock* merge_blocks(FreeBlock* first, FreeBlock* second); + // Values of each used bit in m_flags enum { flags_SelectBit = 1, @@ -403,7 +502,6 @@ class SlabAlloc : public Allocator { std::unique_ptr m_section_bases; size_t m_num_section_bases = 0; AttachMode m_attach_mode = attach_None; - bool m_file_on_streaming_form = false; enum FeeeSpaceState { free_space_Clean, free_space_Dirty, @@ -423,7 +521,6 @@ class SlabAlloc : public Allocator { typedef std::vector slabs; typedef std::vector chunks; slabs m_slabs; - chunks m_free_space; chunks m_free_read_only; bool m_debug_out = false; @@ -436,18 +533,20 @@ class SlabAlloc : public Allocator { mutable size_t version = 1; /// Throws if free-lists are no longer valid. - void consolidate_free_read_only(); + size_t consolidate_free_read_only(); /// Throws if free-lists are no longer valid. const chunks& get_free_read_only() const; /// Throws InvalidDatabase if the file is not a Realm file, if the file is /// corrupted, or if the specified encryption key is incorrect. This /// function will not detect all forms of corruption, though. - void validate_buffer(const char* data, size_t len, const std::string& path, bool is_shared); + void validate_buffer(const char* data, size_t len, const std::string& path); + void throw_header_exception(std::string msg, const Header& header, const std::string& path); + static bool is_file_on_streaming_form(const Header& header); /// Read the top_ref from the given buffer and set m_file_on_streaming_form /// if the buffer contains a file in streaming form - ref_type get_top_ref(const char* data, size_t len); + static ref_type get_top_ref(const char* data, size_t len); class ChunkRefEq; class ChunkRefEndEq; @@ -463,36 +562,6 @@ class SlabAlloc : public Allocator { m_replication = r; } - /// Returns the first section boundary *above* the given position. - size_t get_upper_section_boundary(size_t start_pos) const noexcept; - - /// Returns the first section boundary *at or below* the given position. - size_t get_lower_section_boundary(size_t start_pos) const noexcept; - - /// Returns true if the given position is at a section boundary - bool matches_section_boundary(size_t pos) const noexcept; - - /// Returns the index of the section holding a given address. - /// The section index is determined solely by the minimal section size, - /// and does not necessarily reflect the mapping. A mapping may - /// cover multiple sections - the initial mapping often does. - size_t get_section_index(size_t pos) const noexcept; - - /// Reverse: get the base offset of a section at a given index. Since the - /// computation is very time critical, this method just looks it up in - /// a table. The actual computation and setup of that table is done - /// during initialization with the help of compute_section_base() below. - inline size_t get_section_base(size_t index) const noexcept; - - /// Actually compute the starting offset of a section. Only used to initialize - /// a table of predefined results, which are then used by get_section_base(). - size_t compute_section_base(size_t index) const noexcept; - - /// Find a possible allocation of 'request_size' that will fit into a section - /// which is inside the range from 'start_pos' to 'start_pos'+'free_chunk_size' - /// If found return the position, if not return 0. - size_t find_section_in_range(size_t start_pos, size_t free_chunk_size, size_t request_size) const noexcept; - friend class Group; friend class SharedGroup; friend class GroupWriter; @@ -593,6 +662,33 @@ inline size_t SlabAlloc::get_section_base(size_t index) const noexcept return m_section_bases[index]; } +template +void SlabAlloc::for_all_free_entries(Func f) const +{ + ref_type ref = m_baseline; + for (auto& e : m_slabs) { + BetweenBlocks* bb = reinterpret_cast(e.addr); + REALM_ASSERT(bb->block_before_size == 0); + while (1) { + int size = bb->block_after_size; + f(ref, sizeof(BetweenBlocks)); + ref += sizeof(BetweenBlocks); + if (size == 0) { + break; + } + if (size > 0) { // freeblock. + f(ref, size); + bb = reinterpret_cast(reinterpret_cast(bb) + sizeof(BetweenBlocks) + size); + ref += size; + } + else { + bb = reinterpret_cast(reinterpret_cast(bb) + sizeof(BetweenBlocks) - size); + ref -= size; + } + } + } +} + } // namespace realm #endif // REALM_ALLOC_SLAB_HPP diff --git a/Pods/Realm/include/core/realm/array.hpp b/Pods/Realm/include/core/realm/array.hpp index 53b9977..e0c2684 100644 --- a/Pods/Realm/include/core/realm/array.hpp +++ b/Pods/Realm/include/core/realm/array.hpp @@ -47,7 +47,6 @@ Searching: The main finding function is: #include // unint8_t etc -#include #include #include #include @@ -100,8 +99,8 @@ inline T no0(T v) /// found'. It is similar in function to std::string::npos. const size_t npos = size_t(-1); -// Maximum number of bytes that the payload of an array can be -const size_t max_array_payload = 0x00ffffffL; +const size_t max_array_size = 0x00ffffffL; // Maximum number of elements in an array +const size_t max_array_payload_aligned = 0x07ffffc0L; // Maximum number of bytes that the payload of an array can be /// Alias for realm::npos. const size_t not_found = npos; @@ -117,12 +116,13 @@ class ArrayWriterBase; } -#ifdef REALM_DEBUG struct MemStats { size_t allocated = 0; size_t used = 0; size_t array_count = 0; }; + +#ifdef REALM_DEBUG template std::basic_ostream& operator<<(std::basic_ostream& out, MemStats stats); #endif @@ -343,11 +343,16 @@ class Array : public ArrayParent { bool is_empty() const noexcept; Type get_type() const noexcept; + static void add_to_column(IntegerColumn* column, int64_t value); void insert(size_t ndx, int_fast64_t value); void add(int_fast64_t value); + // Used from ArrayBlob + size_t blob_size() const noexcept; + ref_type blob_replace(size_t begin, size_t end, const char* data, size_t data_size, bool add_zero_term); + /// This function is guaranteed to not throw if the current width is /// sufficient for the specified value (e.g. if you have called /// ensure_minimum_width(value)) and get_alloc().is_read_only(get_ref()) @@ -558,6 +563,7 @@ class Array : public ArrayParent { /// /// This information is guaranteed to be cached in the array accessor. bool has_refs() const noexcept; + void set_has_refs(bool) noexcept; /// This information is guaranteed to be cached in the array accessor. /// @@ -844,17 +850,20 @@ class Array : public ArrayParent { /// FIXME: Belongs in IntegerArray static size_t calc_aligned_byte_size(size_t size, int width); -#ifdef REALM_DEBUG - void print() const; - void verify() const; - typedef size_t (*LeafVerifier)(MemRef, Allocator&); - void verify_bptree(LeafVerifier) const; class MemUsageHandler { public: virtual void handle(ref_type ref, size_t allocated, size_t used) = 0; }; + void report_memory_usage(MemUsageHandler&) const; - void stats(MemStats& stats_dest) const; + + void stats(MemStats& stats_dest) const noexcept; + +#ifdef REALM_DEBUG + void print() const; + void verify() const; + typedef size_t (*LeafVerifier)(MemRef, Allocator&); + void verify_bptree(LeafVerifier) const; typedef void (*LeafDumper)(MemRef, Allocator&, std::ostream&, int level); void dump_bptree_structure(std::ostream&, int level, LeafDumper) const; void to_dot(std::ostream&, StringData title = StringData()) const; @@ -874,8 +883,8 @@ class Array : public ArrayParent { // The encryption layer relies on headers always fitting within a single page. static_assert(header_size == 8, "Header must always fit in entirely on a page"); -private: - Array& operator=(const Array&); // not allowed + Array& operator=(const Array&) = delete; // not allowed + Array(const Array&) = delete; // not allowed protected: typedef bool (*CallbackDummy)(int64_t); @@ -936,7 +945,7 @@ class Array : public ArrayParent { void copy_on_write(); private: - void do_copy_on_write(size_t minimum_size=0); + void do_copy_on_write(size_t minimum_size = 0); void do_ensure_minimum_width(int_fast64_t); template @@ -1006,9 +1015,7 @@ class Array : public ArrayParent { /// log2. Posssible results {0, 1, 2, 4, 8, 16, 32, 64} static size_t bit_width(int64_t value); -#ifdef REALM_DEBUG void report_memory_usage_2(MemUsageHandler&) const; -#endif private: Getter m_getter = nullptr; // cached to avoid indirection @@ -1416,6 +1423,15 @@ inline bool Array::has_refs() const noexcept return m_has_refs; } +inline void Array::set_has_refs(bool value) noexcept +{ + if (m_has_refs != value) { + REALM_ASSERT(!is_read_only()); + m_has_refs = value; + set_header_hasrefs(value); + } +} + inline bool Array::get_context_flag() const noexcept { return m_context_flag; @@ -1423,8 +1439,11 @@ inline bool Array::get_context_flag() const noexcept inline void Array::set_context_flag(bool value) noexcept { - m_context_flag = value; - set_header_context_flag(value); + if (m_context_flag != value) { + REALM_ASSERT(!is_read_only()); + m_context_flag = value; + set_header_context_flag(value); + } } inline ref_type Array::get_ref() const noexcept @@ -1614,7 +1633,7 @@ inline size_t Array::get_capacity_from_header(const char* header) noexcept { typedef unsigned char uchar; const uchar* h = reinterpret_cast(header); - return (size_t(h[0]) << 16) + (size_t(h[1]) << 8) + h[2]; + return (size_t(h[0]) << 19) + (size_t(h[1]) << 11) + (h[2] << 3); } @@ -1711,7 +1730,7 @@ inline void Array::set_header_width(int value, char* header) noexcept inline void Array::set_header_size(size_t value, char* header) noexcept { - REALM_ASSERT_3(value, <=, max_array_payload); + REALM_ASSERT_3(value, <=, max_array_size); typedef unsigned char uchar; uchar* h = reinterpret_cast(header); h[5] = uchar((value >> 16) & 0x000000FF); @@ -1722,12 +1741,12 @@ inline void Array::set_header_size(size_t value, char* header) noexcept // Note: There is a copy of this function is test_alloc.cpp inline void Array::set_header_capacity(size_t value, char* header) noexcept { - REALM_ASSERT_3(value, <=, max_array_payload); + REALM_ASSERT_3(value, <=, (0xffffff << 3)); typedef unsigned char uchar; uchar* h = reinterpret_cast(header); - h[0] = uchar((value >> 16) & 0x000000FF); - h[1] = uchar((value >> 8) & 0x000000FF); - h[2] = uchar(value & 0x000000FF); + h[0] = uchar((value >> 19) & 0x000000FF); + h[1] = uchar((value >> 11) & 0x000000FF); + h[2] = uchar(value >> 3 & 0x000000FF); } diff --git a/Pods/Realm/include/core/realm/array_basic.hpp b/Pods/Realm/include/core/realm/array_basic.hpp index 317f11f..6b9c212 100644 --- a/Pods/Realm/include/core/realm/array_basic.hpp +++ b/Pods/Realm/include/core/realm/array_basic.hpp @@ -33,6 +33,10 @@ class BasicArray : public Array { { } + // Disable copying, this is not allowed. + BasicArray& operator=(const BasicArray&) = delete; + BasicArray(const BasicArray&) = delete; + T get(size_t ndx) const noexcept; bool is_null(size_t ndx) const noexcept; void add(T value); diff --git a/Pods/Realm/include/core/realm/array_basic_tpl.hpp b/Pods/Realm/include/core/realm/array_basic_tpl.hpp index 4eb5bea..b2d542c 100644 --- a/Pods/Realm/include/core/realm/array_basic_tpl.hpp +++ b/Pods/Realm/include/core/realm/array_basic_tpl.hpp @@ -63,12 +63,20 @@ inline MemRef BasicArray::create_array(Array::Type type, bool context_flag, s REALM_ASSERT(!context_flag); MemRef mem = create_array(init_size, allocator); if (init_size) { + // GCC 7.x emits a false-positive strict aliasing warning for this code. Suppress it, since it + // clutters up the build output. See for details. + REALM_DIAG_PUSH(); + REALM_DIAG(ignored "-Wstrict-aliasing"); + BasicArray tmp(allocator); tmp.init_from_mem(mem); - for (size_t i = 0; i < init_size; ++i) { - tmp.set(i, value); + T* p = reinterpret_cast(tmp.m_data); + T* end = p + init_size; + while (p < end) { + *p++ = value; } - return tmp.get_mem(); + + REALM_DIAG_POP(); } return mem; } @@ -208,7 +216,7 @@ void BasicArray::erase(size_t ndx) char* dst_begin = m_data + ndx * m_width; const char* src_begin = dst_begin + m_width; const char* src_end = m_data + m_size * m_width; - std::copy_n(src_begin, src_end - src_begin, dst_begin); + realm::safe_copy_n(src_begin, src_end - src_begin, dst_begin); } // Update size (also in header) @@ -244,7 +252,7 @@ bool BasicArray::compare(const BasicArray& a) const return false; const T* data_1 = reinterpret_cast(m_data); const T* data_2 = reinterpret_cast(a.m_data); - return std::equal(data_1, data_1 + n, data_2); + return realm::safe_equal(data_1, data_1 + n, data_2); } diff --git a/Pods/Realm/include/core/realm/array_binary.hpp b/Pods/Realm/include/core/realm/array_binary.hpp index 414b0df..347c7be 100644 --- a/Pods/Realm/include/core/realm/array_binary.hpp +++ b/Pods/Realm/include/core/realm/array_binary.hpp @@ -60,6 +60,10 @@ class ArrayBinary : public Array { { } + // Disable copying, this is not allowed. + ArrayBinary& operator=(const ArrayBinary&) = delete; + ArrayBinary(const ArrayBinary&) = delete; + /// Create a new empty binary array and attach this accessor to /// it. This does not modify the parent reference information of /// this accessor. @@ -206,10 +210,10 @@ inline void ArrayBinary::truncate(size_t new_size) { REALM_ASSERT_3(new_size, <, m_offsets.size()); - size_t blob_size = new_size ? to_size_t(m_offsets.get(new_size - 1)) : 0; + size_t sz = new_size ? to_size_t(m_offsets.get(new_size - 1)) : 0; m_offsets.truncate(new_size); - m_blob.truncate(blob_size); + m_blob.truncate(sz); if (!legacy_array_type()) m_nulls.truncate(new_size); } diff --git a/Pods/Realm/include/core/realm/array_blob.hpp b/Pods/Realm/include/core/realm/array_blob.hpp index 7fcf181..556d40a 100644 --- a/Pods/Realm/include/core/realm/array_blob.hpp +++ b/Pods/Realm/include/core/realm/array_blob.hpp @@ -33,6 +33,10 @@ class ArrayBlob : public Array { { } + // Disable copying, this is not allowed. + ArrayBlob& operator=(const ArrayBlob&) = delete; + ArrayBlob(const ArrayBlob&) = delete; + const char* get(size_t index) const noexcept; BinaryData get_at(size_t& pos) const noexcept; bool is_null(size_t index) const noexcept; @@ -61,7 +65,6 @@ class ArrayBlob : public Array { static MemRef create_array(size_t init_size, Allocator&); #ifdef REALM_DEBUG - size_t blob_size() const noexcept; void verify() const; void to_dot(std::ostream&, StringData title = StringData()) const; #endif diff --git a/Pods/Realm/include/core/realm/array_blobs_big.hpp b/Pods/Realm/include/core/realm/array_blobs_big.hpp index d931837..cc793fb 100644 --- a/Pods/Realm/include/core/realm/array_blobs_big.hpp +++ b/Pods/Realm/include/core/realm/array_blobs_big.hpp @@ -30,6 +30,10 @@ class ArrayBigBlobs : public Array { explicit ArrayBigBlobs(Allocator&, bool nullable) noexcept; + // Disable copying, this is not allowed. + ArrayBigBlobs& operator=(const ArrayBigBlobs&) = delete; + ArrayBigBlobs(const ArrayBigBlobs&) = delete; + BinaryData get(size_t ndx) const noexcept; BinaryData get_at(size_t ndx, size_t& pos) const noexcept; void set(size_t ndx, BinaryData value, bool add_zero_term = false); @@ -104,8 +108,8 @@ inline BinaryData ArrayBigBlobs::get(size_t ndx) const noexcept const char* blob_header = get_alloc().translate(ref); if (!get_context_flag_from_header(blob_header)) { const char* value = ArrayBlob::get(blob_header, 0); - size_t blob_size = get_size_from_header(blob_header); - return BinaryData(value, blob_size); + size_t sz = get_size_from_header(blob_header); + return BinaryData(value, sz); } return {}; } @@ -119,8 +123,8 @@ inline BinaryData ArrayBigBlobs::get(const char* header, size_t ndx, Allocator& const char* blob_header = alloc.translate(blob_ref); if (!get_context_flag_from_header(blob_header)) { const char* blob_data = Array::get_data_from_header(blob_header); - size_t blob_size = Array::get_size_from_header(blob_header); - return BinaryData(blob_data, blob_size); + size_t sz = Array::get_size_from_header(blob_header); + return BinaryData(blob_data, sz); } return {}; } @@ -129,7 +133,7 @@ inline void ArrayBigBlobs::erase(size_t ndx) { ref_type blob_ref = Array::get_as_ref(ndx); if (blob_ref != 0) { // nothing to destroy if null - Array::destroy(blob_ref, get_alloc()); // Shallow + Array::destroy_deep(blob_ref, get_alloc()); // Deep } Array::erase(ndx); } diff --git a/Pods/Realm/include/core/realm/array_integer.hpp b/Pods/Realm/include/core/realm/array_integer.hpp index 40822a8..e460c53 100644 --- a/Pods/Realm/include/core/realm/array_integer.hpp +++ b/Pods/Realm/include/core/realm/array_integer.hpp @@ -34,6 +34,10 @@ class ArrayInteger : public Array { { } + // Disable copying, this is not allowed. + ArrayInteger& operator=(const ArrayInteger&) = delete; + ArrayInteger(const ArrayInteger&) = delete; + void create(Type type = type_Normal, bool context_flag = false); void add(int64_t value); diff --git a/Pods/Realm/include/core/realm/array_string_long.hpp b/Pods/Realm/include/core/realm/array_string_long.hpp index af9911e..8f5878d 100644 --- a/Pods/Realm/include/core/realm/array_string_long.hpp +++ b/Pods/Realm/include/core/realm/array_string_long.hpp @@ -34,6 +34,10 @@ class ArrayStringLong : public Array { { } + // Disable copying, this is not allowed. + ArrayStringLong& operator=(const ArrayStringLong&) = delete; + ArrayStringLong(const ArrayStringLong&) = delete; + /// Create a new empty long string array and attach this accessor to /// it. This does not modify the parent reference information of /// this accessor. @@ -174,10 +178,10 @@ inline void ArrayStringLong::truncate(size_t new_size) { REALM_ASSERT_3(new_size, <, m_offsets.size()); - size_t blob_size = new_size ? to_size_t(m_offsets.get(new_size - 1)) : 0; + size_t sz = new_size ? to_size_t(m_offsets.get(new_size - 1)) : 0; m_offsets.truncate(new_size); - m_blob.truncate(blob_size); + m_blob.truncate(sz); if (m_nullable) m_nulls.truncate(new_size); } diff --git a/Pods/Realm/include/core/realm/binary_data.hpp b/Pods/Realm/include/core/realm/binary_data.hpp index cc1c283..a184f0c 100644 --- a/Pods/Realm/include/core/realm/binary_data.hpp +++ b/Pods/Realm/include/core/realm/binary_data.hpp @@ -19,14 +19,14 @@ #ifndef REALM_BINARY_DATA_HPP #define REALM_BINARY_DATA_HPP -#include -#include -#include -#include - +#include #include #include -#include + +#include +#include +#include +#include namespace realm { @@ -57,6 +57,10 @@ class BinaryData { template explicit BinaryData(const std::basic_string&); + // BinaryData does not store data, callers must manage their own strings. + template + BinaryData(const std::basic_string&&) = delete; + template explicit operator std::basic_string() const; diff --git a/Pods/Realm/include/core/realm/bptree.hpp b/Pods/Realm/include/core/realm/bptree.hpp index 29dc8e8..aae64de 100644 --- a/Pods/Realm/include/core/realm/bptree.hpp +++ b/Pods/Realm/include/core/realm/bptree.hpp @@ -180,6 +180,10 @@ class BpTreeBase { struct unattached_tag { }; + // Disable copying, this is not allowed. + BpTreeBase& operator=(const BpTreeBase&) = delete; + BpTreeBase(const BpTreeBase&) = delete; + // Accessor concept: Allocator& get_alloc() const noexcept; void destroy() noexcept; @@ -261,6 +265,11 @@ class BpTree : public BpTreeBase { } BpTree(BpTree&&) = default; BpTree& operator=(BpTree&&) = default; + + // Disable copying, this is not allowed. + BpTree& operator=(const BpTree&) = delete; + BpTree(const BpTree&) = delete; + void init_from_ref(Allocator& alloc, ref_type ref); void init_from_mem(Allocator& alloc, MemRef mem); void init_from_parent(); diff --git a/Pods/Realm/include/core/realm/chunked_binary.hpp b/Pods/Realm/include/core/realm/chunked_binary.hpp new file mode 100644 index 0000000..c893b69 --- /dev/null +++ b/Pods/Realm/include/core/realm/chunked_binary.hpp @@ -0,0 +1,125 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2015] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ + +#ifndef REALM_NOINST_CHUNKED_BINARY_HPP +#define REALM_NOINST_CHUNKED_BINARY_HPP + +#include +#include +#include + +#include +#include + + +namespace realm { + +/// ChunkedBinaryData manages a vector of BinaryData. It is used to facilitate +/// extracting large binaries from binary columns and tables. +class ChunkedBinaryData { +public: + + ChunkedBinaryData(); + ChunkedBinaryData(const BinaryData& bd); + ChunkedBinaryData(const BinaryIterator& bd); + ChunkedBinaryData(const BinaryColumn& col, size_t index); + + /// size() returns the number of bytes in the chunked binary. + /// FIXME: This operation is O(n). + size_t size() const; + + /// is_null returns true if the chunked binary has zero chunks or if + /// the first chunk points to the nullptr. + bool is_null() const; + + /// FIXME: O(n) + char operator[](size_t index) const; + + std::string hex_dump(const char* separator = " ", int min_digits = -1) const; + + void write_to(util::ResettableExpandableBufferOutputStream& out) const; + + /// copy_to() copies the chunked binary data to \a buffer of size + /// \a buffer_size starting at \a offset in the ChunkedBinary. + /// copy_to() copies until the end of \a buffer or the end of + /// the ChunkedBinary whichever comes first. + /// copy_to() returns the number of copied bytes. + size_t copy_to(char* buffer, size_t buffer_size, size_t offset) const; + + /// copy_to() allocates a buffer of size() in \a dest and + /// copies the chunked binary data to \a dest. + size_t copy_to(std::unique_ptr& dest) const; + + /// get_first_chunk() is used in situations + /// where it is known that there is exactly one + /// chunk. This is the case if the ChunkedBinary + /// has been constructed from BinaryData. + BinaryData get_first_chunk() const; + +private: + BinaryIterator m_begin; + friend class ChunkedBinaryInputStream; +}; + +// FIXME: When ChunkedBinaryData is moved into Core, this should be moved as well. +class ChunkedBinaryInputStream : public _impl::NoCopyInputStream { +public: + explicit ChunkedBinaryInputStream(const ChunkedBinaryData& chunks) + : m_it(chunks.m_begin) + { + } + + bool next_block(const char*& begin, const char*& end) override + { + BinaryData block = m_it.get_next(); + begin = block.data(); + end = begin + block.size(); + return begin != end; + } + +private: + BinaryIterator m_it; +}; + + +/// Implementation: + + +inline ChunkedBinaryData::ChunkedBinaryData() +{ +} + +inline ChunkedBinaryData::ChunkedBinaryData(const BinaryData& bd) : m_begin{bd} +{ +} + +inline ChunkedBinaryData::ChunkedBinaryData(const BinaryIterator& bd) : m_begin{bd} +{ +} + +inline ChunkedBinaryData::ChunkedBinaryData(const BinaryColumn& col, size_t index) + : m_begin{&col, index} +{ +} + + +} // namespace realm + +#endif // REALM_NOINST_CHUNKED_BINARY_HPP diff --git a/Pods/Realm/include/core/realm/column.hpp b/Pods/Realm/include/core/realm/column.hpp index 9a9fc4b..928fd93 100644 --- a/Pods/Realm/include/core/realm/column.hpp +++ b/Pods/Realm/include/core/realm/column.hpp @@ -34,6 +34,7 @@ #include #include #include +#include namespace realm { @@ -90,7 +91,7 @@ class ColumnRandIterator : public std::iterator operator--(int); ColumnRandIterator operator+(ptrdiff_t movement); ColumnRandIterator operator-(ptrdiff_t movement); - ptrdiff_t operator-(const ColumnRandIterator& rawIterator); + ptrdiff_t operator-(const ColumnRandIterator& right) const; const ColumnDataType operator*() const; const ColumnDataType operator->() const; const ColumnDataType operator[](ptrdiff_t offset) const; @@ -196,6 +197,10 @@ class ColumnBase { { } + // Disable copying, this is not supported. + ColumnBase& operator=(const ColumnBase&) = delete; + ColumnBase(const ColumnBase&) = delete; + // Getter function for index. For integer index, the caller must supply a // buffer that we can store the extracted value in (it may be bitpacked, so // we cannot return a pointer in to the Array as we do with String index). @@ -208,8 +213,7 @@ class ColumnBase { virtual void destroy_search_index() noexcept; virtual const StringIndex* get_search_index() const noexcept; virtual StringIndex* get_search_index() noexcept; - virtual void set_search_index_ref(ref_type, ArrayParent*, size_t ndx_in_parent, bool allow_duplicate_values); - virtual void set_search_index_allow_duplicate_values(bool) noexcept; + virtual void set_search_index_ref(ref_type, ArrayParent*, size_t ndx_in_parent); virtual Allocator& get_alloc() const noexcept = 0; @@ -273,7 +277,7 @@ class ColumnBase { /// the pointer to the subtable accessor at the specified row index if it /// exists, otherwise it returns null. For other column types, this function /// returns null. - virtual Table* get_subtable_accessor(size_t row_ndx) const noexcept; + virtual TableRef get_subtable_accessor(size_t row_ndx) const noexcept; /// Detach and remove the subtable accessor at the specified row if it /// exists. For column types that are unable to contain subtable, this @@ -285,6 +289,7 @@ class ColumnBase { /// See Table::adj_acc_move_over() virtual void adj_acc_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept; virtual void adj_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept; + virtual void adj_acc_move_row(size_t from_ndx, size_t to_ndx) noexcept; virtual void adj_acc_merge_rows(size_t old_row_ndx, size_t new_row_ndx) noexcept; virtual void adj_acc_clear_root_table() noexcept; @@ -503,8 +508,7 @@ class ColumnBaseWithIndex : public ColumnBase { return m_search_index.get(); } void destroy_search_index() noexcept override; - void set_search_index_ref(ref_type ref, ArrayParent* parent, size_t ndx_in_parent, - bool allow_duplicate_valaues) final; + void set_search_index_ref(ref_type ref, ArrayParent* parent, size_t ndx_in_parent) final; StringIndex* create_search_index() override = 0; protected: @@ -805,11 +809,7 @@ inline StringIndex* ColumnBase::get_search_index() noexcept return nullptr; } -inline void ColumnBase::set_search_index_ref(ref_type, ArrayParent*, size_t, bool) -{ -} - -inline void ColumnBase::set_search_index_allow_duplicate_values(bool) noexcept +inline void ColumnBase::set_search_index_ref(ref_type, ArrayParent*, size_t) { } @@ -818,11 +818,6 @@ inline void ColumnBase::discard_child_accessors() noexcept do_discard_child_accessors(); } -inline Table* ColumnBase::get_subtable_accessor(size_t) const noexcept -{ - return 0; -} - inline void ColumnBase::discard_subtable_accessor(size_t) noexcept { // Noop @@ -848,6 +843,11 @@ inline void ColumnBase::adj_acc_swap_rows(size_t, size_t) noexcept // Noop } +inline void ColumnBase::adj_acc_move_row(size_t, size_t) noexcept +{ + // Noop +} + inline void ColumnBase::adj_acc_merge_rows(size_t, size_t) noexcept { // Noop @@ -1813,9 +1813,9 @@ ColumnRandIterator ColumnRandIterator::operator- } template -ptrdiff_t ColumnRandIterator::operator-(const ColumnRandIterator& other) +ptrdiff_t ColumnRandIterator::operator-(const ColumnRandIterator& right) const { - return m_col_ndx - other.m_col_ndx; + return m_col_ndx - right.m_col_ndx; } template diff --git a/Pods/Realm/include/core/realm/column_backlink.hpp b/Pods/Realm/include/core/realm/column_backlink.hpp index 11ba6e5..cbbe9e6 100644 --- a/Pods/Realm/include/core/realm/column_backlink.hpp +++ b/Pods/Realm/include/core/realm/column_backlink.hpp @@ -71,6 +71,8 @@ class BacklinkColumn : public IntegerColumn, public ArrayParent { void adj_acc_erase_row(size_t) noexcept override; void adj_acc_move_over(size_t, size_t) noexcept override; void adj_acc_swap_rows(size_t, size_t) noexcept override; + void adj_acc_move_row(size_t, size_t) noexcept override; + void adj_acc_merge_rows(size_t, size_t) noexcept override; void adj_acc_clear_root_table() noexcept override; void mark(int) noexcept override; @@ -187,6 +189,22 @@ inline void BacklinkColumn::adj_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2 tf::mark(*m_origin_table); } +inline void BacklinkColumn::adj_acc_move_row(size_t from_ndx, size_t to_ndx) noexcept +{ + Column::adj_acc_move_row(from_ndx, to_ndx); + + using tf = _impl::TableFriend; + tf::mark(*m_origin_table); +} + +inline void BacklinkColumn::adj_acc_merge_rows(size_t old_row_ndx, size_t new_row_ndx) noexcept +{ + Column::adj_acc_merge_rows(old_row_ndx, new_row_ndx); + + using tf = _impl::TableFriend; + tf::mark(*m_origin_table); +} + inline void BacklinkColumn::adj_acc_clear_root_table() noexcept { IntegerColumn::adj_acc_clear_root_table(); diff --git a/Pods/Realm/include/core/realm/column_binary.hpp b/Pods/Realm/include/core/realm/column_binary.hpp index c891297..7802e03 100644 --- a/Pods/Realm/include/core/realm/column_binary.hpp +++ b/Pods/Realm/include/core/realm/column_binary.hpp @@ -145,7 +145,7 @@ class BinaryIterator { { } - BinaryIterator(BinaryColumn* col, size_t ndx) + BinaryIterator(const BinaryColumn* col, size_t ndx) : m_binary_col(col) , m_ndx(ndx) { @@ -169,7 +169,7 @@ class BinaryIterator { private: bool end_of_data = false; - BinaryColumn* m_binary_col = nullptr; + const BinaryColumn* m_binary_col = nullptr; size_t m_ndx = 0; size_t m_pos = 0; BinaryData m_binary; diff --git a/Pods/Realm/include/core/realm/column_fwd.hpp b/Pods/Realm/include/core/realm/column_fwd.hpp index 1eecf01..5b93cdc 100644 --- a/Pods/Realm/include/core/realm/column_fwd.hpp +++ b/Pods/Realm/include/core/realm/column_fwd.hpp @@ -32,6 +32,7 @@ class SubtableColumn; class MixedColumn; class LinkColumn; class LinkListColumn; +class TimestampColumn; // Templated classes template diff --git a/Pods/Realm/include/core/realm/column_link.hpp b/Pods/Realm/include/core/realm/column_link.hpp index 18471bb..185a6c3 100644 --- a/Pods/Realm/include/core/realm/column_link.hpp +++ b/Pods/Realm/include/core/realm/column_link.hpp @@ -117,6 +117,8 @@ inline size_t LinkColumn::set_link(size_t row_ndx, size_t target_row_ndx) { int_fast64_t old_value = LinkColumnBase::get(row_ndx); size_t old_target_row_ndx = to_size_t(old_value) - size_t(1); + if (old_target_row_ndx == target_row_ndx) + return old_target_row_ndx; // Nothing to do if (old_value != 0) m_backlink_column->remove_one_backlink(old_target_row_ndx, row_ndx); // Throws diff --git a/Pods/Realm/include/core/realm/column_linkbase.hpp b/Pods/Realm/include/core/realm/column_linkbase.hpp index cff46b2..fa67fab 100644 --- a/Pods/Realm/include/core/realm/column_linkbase.hpp +++ b/Pods/Realm/include/core/realm/column_linkbase.hpp @@ -61,6 +61,7 @@ class LinkColumnBase : public IntegerColumn { void adj_acc_erase_row(size_t) noexcept override; void adj_acc_move_over(size_t, size_t) noexcept override; void adj_acc_swap_rows(size_t, size_t) noexcept override; + void adj_acc_move_row(size_t, size_t) noexcept override; void adj_acc_clear_root_table() noexcept override; void mark(int) noexcept override; void refresh_accessor_tree(size_t, const Spec&) override; @@ -164,6 +165,14 @@ inline void LinkColumnBase::adj_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2 tf::mark(*m_target_table); } +inline void LinkColumnBase::adj_acc_move_row(size_t from_ndx, size_t to_ndx) noexcept +{ + IntegerColumn::adj_acc_move_row(from_ndx, to_ndx); + + using tf = _impl::TableFriend; + tf::mark(*m_target_table); +} + inline void LinkColumnBase::adj_acc_clear_root_table() noexcept { IntegerColumn::adj_acc_clear_root_table(); diff --git a/Pods/Realm/include/core/realm/column_linklist.hpp b/Pods/Realm/include/core/realm/column_linklist.hpp index 445066e..3dab1b3 100644 --- a/Pods/Realm/include/core/realm/column_linklist.hpp +++ b/Pods/Realm/include/core/realm/column_linklist.hpp @@ -45,6 +45,7 @@ class TransactLogConvenientEncoder; class LinkListColumn : public LinkColumnBase, public ArrayParent { public: using LinkColumnBase::LinkColumnBase; + using value_type = ConstLinkViewRef; LinkListColumn(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx); ~LinkListColumn() noexcept override; @@ -79,6 +80,7 @@ class LinkListColumn : public LinkColumnBase, public ArrayParent { void adj_acc_erase_row(size_t) noexcept override; void adj_acc_move_over(size_t, size_t) noexcept override; void adj_acc_swap_rows(size_t, size_t) noexcept override; + void adj_acc_move_row(size_t, size_t) noexcept override; void adj_acc_merge_rows(size_t, size_t) noexcept override; void refresh_accessor_tree(size_t, const Spec&) override; @@ -141,6 +143,9 @@ class LinkListColumn : public LinkColumnBase, public ArrayParent { template void adj_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept; + template + void adj_move(size_t from_ndx, size_t to_ndx) noexcept; + template void adj_swap(size_t row_ndx_1, size_t row_ndx_2) noexcept; diff --git a/Pods/Realm/include/core/realm/column_mixed.hpp b/Pods/Realm/include/core/realm/column_mixed.hpp index 326edad..5f74c41 100644 --- a/Pods/Realm/include/core/realm/column_mixed.hpp +++ b/Pods/Realm/include/core/realm/column_mixed.hpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #include @@ -93,20 +92,17 @@ class MixedColumn : public ColumnBaseSimple { /// contain a subtable. size_t get_subtable_size(size_t row_ndx) const noexcept; - Table* get_subtable_accessor(size_t row_ndx) const noexcept override; + TableRef get_subtable_accessor(size_t row_ndx) const noexcept override; void discard_subtable_accessor(size_t row_ndx) noexcept override; /// If the value at the specified index is a subtable, return a - /// pointer to that accessor for that subtable. Otherwise return + /// TableRef to that accessor for that subtable. Otherwise return /// null. The accessor will be created if it does not already /// exist. - /// - /// The returned table pointer must **always** end up being - /// wrapped in some instantiation of BasicTableRef<>. - Table* get_subtable_ptr(size_t row_ndx); + TableRef get_subtable_tableref(size_t row_ndx); - const Table* get_subtable_ptr(size_t subtable_ndx) const; + ConstTableRef get_subtable_tableref(size_t subtable_ndx) const; void set_int(size_t ndx, int64_t value); void set_bool(size_t ndx, bool value); @@ -156,6 +152,7 @@ class MixedColumn : public ColumnBaseSimple { void adj_acc_erase_row(size_t) noexcept override; void adj_acc_move_over(size_t, size_t) noexcept override; void adj_acc_swap_rows(size_t, size_t) noexcept override; + void adj_acc_move_row(size_t, size_t) noexcept override; void adj_acc_clear_root_table() noexcept override; void mark(int) noexcept override; void refresh_accessor_tree(size_t, const Spec&) override; @@ -250,7 +247,7 @@ class MixedColumn::RefsColumn : public SubtableColumnBase { RefsColumn(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx); ~RefsColumn() noexcept override; - using SubtableColumnBase::get_subtable_ptr; + using SubtableColumnBase::get_subtable_tableref; void refresh_accessor_tree(size_t, const Spec&) override; diff --git a/Pods/Realm/include/core/realm/column_mixed_tpl.hpp b/Pods/Realm/include/core/realm/column_mixed_tpl.hpp index ca5d4d4..5afdddc 100644 --- a/Pods/Realm/include/core/realm/column_mixed_tpl.hpp +++ b/Pods/Realm/include/core/realm/column_mixed_tpl.hpp @@ -16,6 +16,9 @@ * **************************************************************************/ +#include +#include + namespace realm { inline MixedColumn::MixedColumn(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx) @@ -39,6 +42,11 @@ inline void MixedColumn::adj_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2) n m_data->adj_acc_swap_rows(row_ndx_1, row_ndx_2); } +inline void MixedColumn::adj_acc_move_row(size_t from_ndx, size_t to_ndx) noexcept +{ + m_data->adj_acc_move_row(from_ndx, to_ndx); +} + inline void MixedColumn::adj_acc_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept { m_data->adj_acc_move_over(from_row_ndx, to_row_ndx); @@ -65,7 +73,7 @@ inline size_t MixedColumn::get_subtable_size(size_t row_ndx) const noexcept return _impl::TableFriend::get_size_from_ref(top_ref, m_data->get_alloc()); } -inline Table* MixedColumn::get_subtable_accessor(size_t row_ndx) const noexcept +inline TableRef MixedColumn::get_subtable_accessor(size_t row_ndx) const noexcept { return m_data->get_subtable_accessor(row_ndx); } @@ -75,17 +83,17 @@ inline void MixedColumn::discard_subtable_accessor(size_t row_ndx) noexcept m_data->discard_subtable_accessor(row_ndx); } -inline Table* MixedColumn::get_subtable_ptr(size_t row_ndx) +inline TableRef MixedColumn::get_subtable_tableref(size_t row_ndx) { REALM_ASSERT_3(row_ndx, <, m_types->size()); if (m_types->get(row_ndx) != type_Table) - return 0; - return m_data->get_subtable_ptr(row_ndx); // Throws + return {}; + return m_data->get_subtable_tableref(row_ndx); // Throws } -inline const Table* MixedColumn::get_subtable_ptr(size_t subtable_ndx) const +inline ConstTableRef MixedColumn::get_subtable_tableref(size_t subtable_ndx) const { - return const_cast(this)->get_subtable_ptr(subtable_ndx); + return const_cast(this)->get_subtable_tableref(subtable_ndx); } inline void MixedColumn::discard_child_accessors() noexcept @@ -467,16 +475,8 @@ inline void MixedColumn::refresh_accessor_tree(size_t col_ndx, const Spec& spec) get_root_array()->init_from_parent(); m_types->refresh_accessor_tree(col_ndx, spec); // Throws m_data->refresh_accessor_tree(col_ndx, spec); // Throws - if (m_binary_data) { - REALM_ASSERT_3(get_root_array()->size(), >=, 3); - m_binary_data->refresh_accessor_tree(col_ndx, spec); // Throws - } - if (m_timestamp_data) { - REALM_ASSERT_3(get_root_array()->size(), >=, 4); - m_timestamp_data->refresh_accessor_tree(col_ndx, spec); // Throws - } - + m_binary_data.reset(); // See if m_binary_data needs to be created. if (get_root_array()->size() >= 3) { ref_type ref = get_root_array()->get_as_ref(2); @@ -484,6 +484,7 @@ inline void MixedColumn::refresh_accessor_tree(size_t col_ndx, const Spec& spec) m_binary_data->set_parent(get_root_array(), 2); } + m_timestamp_data.reset(); // See if m_timestamp_data needs to be created. if (get_root_array()->size() >= 4) { ref_type ref = get_root_array()->get_as_ref(3); @@ -493,13 +494,21 @@ inline void MixedColumn::refresh_accessor_tree(size_t col_ndx, const Spec& spec) m_timestamp_data.reset(new TimestampColumn(true /*fixme*/, get_alloc(), ref)); // Throws m_timestamp_data->set_parent(get_root_array(), 3); } + + if (m_binary_data) { + REALM_ASSERT_3(get_root_array()->size(), >=, 3); + m_binary_data->refresh_accessor_tree(col_ndx, spec); // Throws + } + if (m_timestamp_data) { + REALM_ASSERT_3(get_root_array()->size(), >=, 4); + m_timestamp_data->refresh_accessor_tree(col_ndx, spec); // Throws + } } inline void MixedColumn::RefsColumn::refresh_accessor_tree(size_t col_ndx, const Spec& spec) { SubtableColumnBase::refresh_accessor_tree(col_ndx, spec); // Throws - size_t spec_ndx_in_parent = 0; // Ignored because these are root tables - m_subtable_map.refresh_accessor_tree(spec_ndx_in_parent); // Throws + m_subtable_map.refresh_accessor_tree(); // Throws } } // namespace realm diff --git a/Pods/Realm/include/core/realm/column_string.hpp b/Pods/Realm/include/core/realm/column_string.hpp index 50bab6f..e7693ee 100644 --- a/Pods/Realm/include/core/realm/column_string.hpp +++ b/Pods/Realm/include/core/realm/column_string.hpp @@ -92,8 +92,7 @@ class StringColumn : public ColumnBaseSimple { // Search index StringData get_index_data(size_t ndx, StringIndex::StringConversionBuffer& buffer) const noexcept final; bool has_search_index() const noexcept override; - void set_search_index_ref(ref_type, ArrayParent*, size_t, bool) override; - void set_search_index_allow_duplicate_values(bool) noexcept override; + void set_search_index_ref(ref_type, ArrayParent*, size_t) override; StringIndex* get_search_index() noexcept override; const StringIndex* get_search_index() const noexcept override; std::unique_ptr release_search_index() noexcept; diff --git a/Pods/Realm/include/core/realm/column_string_enum.hpp b/Pods/Realm/include/core/realm/column_string_enum.hpp index be27ba0..a73fe0c 100644 --- a/Pods/Realm/include/core/realm/column_string_enum.hpp +++ b/Pods/Realm/include/core/realm/column_string_enum.hpp @@ -85,6 +85,7 @@ class StringEnumColumn : public IntegerColumn { void insert(size_t ndx, StringData value); void erase(size_t row_ndx); void move_last_over(size_t row_ndx); + void swap_rows(size_t row_ndx_1, size_t row_ndx_2) override; void clear(); bool is_nullable() const noexcept final; @@ -111,7 +112,6 @@ class StringEnumColumn : public IntegerColumn { // Search index StringData get_index_data(size_t ndx, StringIndex::StringConversionBuffer& buffer) const noexcept final; - void set_search_index_allow_duplicate_values(bool) noexcept override; bool supports_search_index() const noexcept final { return true; diff --git a/Pods/Realm/include/core/realm/column_table.hpp b/Pods/Realm/include/core/realm/column_table.hpp index ca5e38b..0d424c2 100644 --- a/Pods/Realm/include/core/realm/column_table.hpp +++ b/Pods/Realm/include/core/realm/column_table.hpp @@ -20,6 +20,7 @@ #define REALM_COLUMN_TABLE_HPP #include +#include #include #include @@ -39,7 +40,7 @@ class SubtableColumnBase : public IntegerColumn, public Table::Parent { static ref_type create(Allocator&, size_t size = 0); - Table* get_subtable_accessor(size_t) const noexcept override; + TableRef get_subtable_accessor(size_t) const noexcept override; void insert_rows(size_t, size_t, size_t, bool) override; void erase_rows(size_t, size_t, size_t, bool) override; @@ -53,6 +54,7 @@ class SubtableColumnBase : public IntegerColumn, public Table::Parent { void adj_acc_move_over(size_t, size_t) noexcept override; void adj_acc_clear_root_table() noexcept override; void adj_acc_swap_rows(size_t, size_t) noexcept override; + void adj_acc_move_row(size_t, size_t) noexcept override; void mark(int) noexcept override; bool supports_search_index() const noexcept override { @@ -62,6 +64,10 @@ class SubtableColumnBase : public IntegerColumn, public Table::Parent { { return nullptr; } + bool is_null(size_t ndx) const noexcept override + { + return get_as_ref(ndx) == 0; + } void verify() const override; void verify(const Table&, size_t) const override; @@ -72,9 +78,6 @@ class SubtableColumnBase : public IntegerColumn, public Table::Parent { Table* const m_table; struct SubtableMap { - ~SubtableMap() noexcept - { - } bool empty() const noexcept { return m_entries.empty(); @@ -103,11 +106,15 @@ class SubtableColumnBase : public IntegerColumn, public Table::Parent { bool adj_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept; template void adj_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept; + template + void adj_move_row(size_t from_ndx, size_t to_ndx) noexcept; + void adj_set_null(size_t row_ndx) noexcept; void update_accessors(const size_t* col_path_begin, const size_t* col_path_end, _impl::TableFriend::AccessorUpdater&); void recursive_mark() noexcept; - void refresh_accessor_tree(size_t spec_ndx_in_parent); + void refresh_accessor_tree(); + void verify(const SubtableColumn& parent); private: struct SubtableEntry { @@ -126,18 +133,16 @@ class SubtableColumnBase : public IntegerColumn, public Table::Parent { /// additional referece count on `*m_table` when, and only when the map is /// non-empty. mutable SubtableMap m_subtable_map; + mutable std::recursive_mutex m_subtable_map_lock; SubtableColumnBase(Allocator&, ref_type, Table*, size_t column_ndx); - /// Get a pointer to the accessor of the specified subtable. The + /// Get a TableRef to the accessor of the specified subtable. The /// accessor will be created if it does not already exist. /// - /// The returned table pointer must **always** end up being - /// wrapped in some instantiation of BasicTableRef<>. - /// /// NOTE: This method must be used only for subtables with /// independent specs, i.e. for elements of a MixedColumn. - Table* get_subtable_ptr(size_t subtable_ndx); + TableRef get_subtable_tableref(size_t subtable_ndx); // Overriding method in ArrayParent void update_child_ref(size_t, ref_type) override; @@ -151,6 +156,10 @@ class SubtableColumnBase : public IntegerColumn, public Table::Parent { // Overriding method in Table::Parent void child_accessor_destroyed(Table*) noexcept override; + // Overriding method in Table::Parent + std::recursive_mutex* get_accessor_management_lock() noexcept override + { return &m_subtable_map_lock; } + /// Assumes that the two tables have the same spec. static bool compare_subtable_rows(const Table&, const Table&); @@ -179,6 +188,7 @@ class SubtableColumnBase : public IntegerColumn, public Table::Parent { class SubtableColumn : public SubtableColumnBase { public: + using value_type = ConstTableRef; /// Create a subtable column accessor and attach it to a /// preexisting underlying structure of arrays. /// @@ -199,16 +209,28 @@ class SubtableColumn : public SubtableColumnBase { { } + // Overriding method in Table::Parent + Spec* get_subtable_spec() noexcept override; + size_t get_subtable_size(size_t ndx) const noexcept; - /// Get a pointer to the accessor of the specified subtable. The + /// Get a TableRef to the accessor of the specified subtable. The /// accessor will be created if it does not already exist. - /// - /// The returned table pointer must **always** end up being - /// wrapped in some instantiation of BasicTableRef<>. - Table* get_subtable_ptr(size_t subtable_ndx); + TableRef get_subtable_tableref(size_t subtable_ndx); - const Table* get_subtable_ptr(size_t subtable_ndx) const; + ConstTableRef get_subtable_tableref(size_t subtable_ndx) const; + + /// This is to be used by the query system that does not need to + /// modify the subtable. Will return a ref object containing a + /// nullptr if there is no table object yet. + ConstTableRef get(size_t subtable_ndx) const + { + int64_t ref = IntegerColumn::get(subtable_ndx); + if (ref) + return get_subtable_tableref(subtable_ndx); + else + return {}; + } // When passing a table to add() or insert() it is assumed that // the table spec is compatible with this column. The number of @@ -220,6 +242,7 @@ class SubtableColumn : public SubtableColumnBase { void insert(size_t ndx, const Table* value = nullptr); void set(size_t ndx, const Table*); void clear_table(size_t ndx); + void set_null(size_t ndx) override; using SubtableColumnBase::insert; @@ -230,6 +253,7 @@ class SubtableColumn : public SubtableColumnBase { bool compare_table(const SubtableColumn&) const; void refresh_accessor_tree(size_t, const Spec&) override; + void refresh_subtable_map(); #ifdef REALM_DEBUG void verify(const Table&, size_t) const override; @@ -251,12 +275,10 @@ class SubtableColumn : public SubtableColumnBase { // Implementation // Overriding virtual method of Column. -inline void SubtableColumnBase::insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, - bool insert_nulls) +inline void SubtableColumnBase::insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, bool) { REALM_ASSERT_DEBUG(prior_num_rows == size()); REALM_ASSERT(row_ndx <= prior_num_rows); - REALM_ASSERT(!insert_nulls); size_t row_ndx_2 = (row_ndx == prior_num_rows ? realm::npos : row_ndx); int_fast64_t value = 0; @@ -269,6 +291,7 @@ inline void SubtableColumnBase::erase_rows(size_t row_ndx, size_t num_rows_to_er { IntegerColumn::erase_rows(row_ndx, num_rows_to_erase, prior_num_rows, broken_reciprocal_backlinks); // Throws + std::lock_guard lg(m_subtable_map_lock); const bool fix_ndx_in_parent = true; bool last_entry_removed = m_subtable_map.adj_erase_rows(row_ndx, num_rows_to_erase); typedef _impl::TableFriend tf; @@ -282,6 +305,7 @@ inline void SubtableColumnBase::move_last_row_over(size_t row_ndx, size_t prior_ { IntegerColumn::move_last_row_over(row_ndx, prior_num_rows, broken_reciprocal_backlinks); // Throws + std::lock_guard lg(m_subtable_map_lock); const bool fix_ndx_in_parent = true; size_t last_row_ndx = prior_num_rows - 1; bool last_entry_removed = m_subtable_map.adj_move_over(last_row_ndx, row_ndx); @@ -304,14 +328,17 @@ inline void SubtableColumnBase::swap_rows(size_t row_ndx_1, size_t row_ndx_2) { IntegerColumn::swap_rows(row_ndx_1, row_ndx_2); // Throws + std::lock_guard lg(m_subtable_map_lock); const bool fix_ndx_in_parent = true; m_subtable_map.adj_swap_rows(row_ndx_1, row_ndx_2); } inline void SubtableColumnBase::mark(int type) noexcept { - if (type & mark_Recursive) + if (type & mark_Recursive) { + std::lock_guard lg(m_subtable_map_lock); m_subtable_map.recursive_mark(); + } } inline void SubtableColumnBase::adj_acc_insert_rows(size_t row_ndx, size_t num_rows) noexcept @@ -320,6 +347,7 @@ inline void SubtableColumnBase::adj_acc_insert_rows(size_t row_ndx, size_t num_r // accessor hierarchy. This means in particular that it cannot access the // underlying node structure. See AccessorConsistencyLevels. + std::lock_guard lg(m_subtable_map_lock); const bool fix_ndx_in_parent = false; m_subtable_map.adj_insert_rows(row_ndx, num_rows); } @@ -330,6 +358,7 @@ inline void SubtableColumnBase::adj_acc_erase_row(size_t row_ndx) noexcept // accessor hierarchy. This means in particular that it cannot access the // underlying node structure. See AccessorConsistencyLevels. + std::lock_guard lg(m_subtable_map_lock); const bool fix_ndx_in_parent = false; size_t num_rows_erased = 1; bool last_entry_removed = m_subtable_map.adj_erase_rows(row_ndx, num_rows_erased); @@ -344,6 +373,7 @@ inline void SubtableColumnBase::adj_acc_move_over(size_t from_row_ndx, size_t to // accessor hierarchy. This means in particular that it cannot access the // underlying node structure. See AccessorConsistencyLevels. + std::lock_guard lg(m_subtable_map_lock); const bool fix_ndx_in_parent = false; bool last_entry_removed = m_subtable_map.adj_move_over(from_row_ndx, to_row_ndx); typedef _impl::TableFriend tf; @@ -363,17 +393,25 @@ inline void SubtableColumnBase::adj_acc_clear_root_table() noexcept inline void SubtableColumnBase::adj_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept { + std::lock_guard lg(m_subtable_map_lock); const bool fix_ndx_in_parent = false; m_subtable_map.adj_swap_rows(row_ndx_1, row_ndx_2); } -inline Table* SubtableColumnBase::get_subtable_accessor(size_t row_ndx) const noexcept +inline void SubtableColumnBase::adj_acc_move_row(size_t from_ndx, size_t to_ndx) noexcept +{ + std::lock_guard lg(m_subtable_map_lock); + const bool fix_ndx_in_parent = false; + m_subtable_map.adj_move_row(from_ndx, to_ndx); +} + +inline TableRef SubtableColumnBase::get_subtable_accessor(size_t row_ndx) const noexcept { // This function must assume no more than minimal consistency of the // accessor hierarchy. This means in particular that it cannot access the // underlying node structure. See AccessorConsistencyLevels. - - Table* subtable = m_subtable_map.find(row_ndx); + std::lock_guard lg(m_subtable_map_lock); + TableRef subtable(m_subtable_map.find(row_ndx)); return subtable; } @@ -383,6 +421,7 @@ inline void SubtableColumnBase::discard_subtable_accessor(size_t row_ndx) noexce // accessor hierarchy. This means in particular that it cannot access the // underlying node structure. See AccessorConsistencyLevels. + std::lock_guard lg(m_subtable_map_lock); bool last_entry_removed = m_subtable_map.detach_and_remove(row_ndx); typedef _impl::TableFriend tf; if (last_entry_removed) @@ -493,6 +532,46 @@ void SubtableColumnBase::SubtableMap::adj_swap_rows(size_t row_ndx_1, size_t row } } + +template +void SubtableColumnBase::SubtableMap::adj_move_row(size_t from_ndx, size_t to_ndx) noexcept +{ + using tf = _impl::TableFriend; + for (auto& entry : m_entries) { + if (entry.m_subtable_ndx == from_ndx) { + entry.m_subtable_ndx = to_ndx; + if (fix_ndx_in_parent) + tf::set_ndx_in_parent(*(entry.m_table), entry.m_subtable_ndx); + } + else { + if (from_ndx < to_ndx) { + // shift the range (from, to] down one + if (entry.m_subtable_ndx <= to_ndx && entry.m_subtable_ndx > from_ndx) { + entry.m_subtable_ndx--; + if (fix_ndx_in_parent) { + tf::set_ndx_in_parent(*(entry.m_table), entry.m_subtable_ndx); + } + } + } else if (from_ndx > to_ndx) { + // shift the range (from, to] up one + if (entry.m_subtable_ndx >= to_ndx && entry.m_subtable_ndx < from_ndx) { + entry.m_subtable_ndx++; + if (fix_ndx_in_parent) { + tf::set_ndx_in_parent(*(entry.m_table), entry.m_subtable_ndx); + } + } + } + } + } +} + +inline void SubtableColumnBase::SubtableMap::adj_set_null(size_t row_ndx) noexcept +{ + Table* table = find(row_ndx); + if (table) + _impl::TableFriend::refresh_accessor_tree(*table); +} + inline SubtableColumnBase::SubtableColumnBase(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx) : IntegerColumn(alloc, ref, column_ndx) // Throws , m_table(table) @@ -501,7 +580,7 @@ inline SubtableColumnBase::SubtableColumnBase(Allocator& alloc, ref_type ref, Ta inline void SubtableColumnBase::update_child_ref(size_t child_ndx, ref_type new_ref) { - set(child_ndx, new_ref); + set_as_ref(child_ndx, new_ref); } inline ref_type SubtableColumnBase::get_child_ref(size_t child_ndx) const noexcept @@ -511,6 +590,7 @@ inline ref_type SubtableColumnBase::get_child_ref(size_t child_ndx) const noexce inline void SubtableColumnBase::discard_child_accessors() noexcept { + std::lock_guard lg(m_subtable_map_lock); bool last_entry_removed = m_subtable_map.detach_and_remove_all(); if (last_entry_removed && m_table) _impl::TableFriend::unbind_ptr(*m_table); @@ -573,28 +653,40 @@ inline SubtableColumn::SubtableColumn(Allocator& alloc, ref_type ref, Table* tab { } -inline const Table* SubtableColumn::get_subtable_ptr(size_t subtable_ndx) const +inline ConstTableRef SubtableColumn::get_subtable_tableref(size_t subtable_ndx) const { - return const_cast(this)->get_subtable_ptr(subtable_ndx); + return const_cast(this)->get_subtable_tableref(subtable_ndx); } inline void SubtableColumn::refresh_accessor_tree(size_t col_ndx, const Spec& spec) { SubtableColumnBase::refresh_accessor_tree(col_ndx, spec); // Throws m_subspec_ndx = spec.get_subspec_ndx(col_ndx); - m_subtable_map.refresh_accessor_tree(m_subspec_ndx); // Throws + std::lock_guard lg(m_subtable_map_lock); + m_subtable_map.refresh_accessor_tree(); // Throws +} + +inline void SubtableColumn::refresh_subtable_map() +{ + std::lock_guard lg(m_subtable_map_lock); + m_subtable_map.refresh_accessor_tree(); // Throws } inline size_t SubtableColumn::get_subspec_ndx() const noexcept { if (REALM_UNLIKELY(m_subspec_ndx == realm::npos)) { typedef _impl::TableFriend tf; - const Spec& spec = tf::get_spec(*m_table); - m_subspec_ndx = spec.get_subspec_ndx(get_column_index()); + m_subspec_ndx = tf::get_spec(*m_table).get_subspec_ndx(get_column_index()); } return m_subspec_ndx; } +inline Spec* SubtableColumn::get_subtable_spec() noexcept +{ + typedef _impl::TableFriend tf; + return tf::get_spec(*m_table).get_subtable_spec(get_column_index()); +} + } // namespace realm diff --git a/Pods/Realm/include/core/realm/column_timestamp.hpp b/Pods/Realm/include/core/realm/column_timestamp.hpp index fa48859..d5c4859 100644 --- a/Pods/Realm/include/core/realm/column_timestamp.hpp +++ b/Pods/Realm/include/core/realm/column_timestamp.hpp @@ -65,8 +65,7 @@ class TimestampColumn : public ColumnBaseSimple { return m_search_index.get(); } void destroy_search_index() noexcept override; - void set_search_index_ref(ref_type ref, ArrayParent* parent, size_t ndx_in_parent, - bool allow_duplicate_values) final; + void set_search_index_ref(ref_type ref, ArrayParent* parent, size_t ndx_in_parent) final; void populate_search_index(); StringIndex* create_search_index() override; bool supports_search_index() const noexcept final diff --git a/Pods/Realm/include/core/realm/column_type_traits.hpp b/Pods/Realm/include/core/realm/column_type_traits.hpp index df9c236..39a3208 100644 --- a/Pods/Realm/include/core/realm/column_type_traits.hpp +++ b/Pods/Realm/include/core/realm/column_type_traits.hpp @@ -22,6 +22,7 @@ #include #include #include +#include namespace realm { @@ -105,12 +106,8 @@ struct ColumnTypeTraits> : ColumnTypeTraits struct ColumnTypeTraits { - using column_type = StringEnumColumn; - using leaf_type = ArrayInteger; - using sum_type = int64_t; static const DataType id = type_String; static const ColumnType column_id = col_type_String; - static const ColumnType real_column_type = col_type_String; }; template <> diff --git a/Pods/Realm/include/core/realm/data_type.hpp b/Pods/Realm/include/core/realm/data_type.hpp index 580982f..859d1cf 100644 --- a/Pods/Realm/include/core/realm/data_type.hpp +++ b/Pods/Realm/include/core/realm/data_type.hpp @@ -19,8 +19,21 @@ #ifndef REALM_DATA_TYPE_HPP #define REALM_DATA_TYPE_HPP +#include + namespace realm { +class StringData; +class BinaryData; + +typedef int64_t Int; +typedef bool Bool; +typedef float Float; +typedef double Double; +typedef realm::StringData String; +typedef realm::BinaryData Binary; + + // Note: Value assignments must be kept in sync with // Note: Value assignments must be kept in sync with // Note: Value assignments must be kept in sync with diff --git a/Pods/Realm/include/core/realm/descriptor.hpp b/Pods/Realm/include/core/realm/descriptor.hpp index 10433eb..9d37de5 100644 --- a/Pods/Realm/include/core/realm/descriptor.hpp +++ b/Pods/Realm/include/core/realm/descriptor.hpp @@ -89,8 +89,8 @@ class Descriptor : public std::enable_shared_from_this { /// returns `not_found`. size_t get_column_index(StringData name) const noexcept; - /// Get the index of the column to which links in the column at the - /// specified index refer. + /// Get the index of the table to which links in the column at the specified + /// index refer. /// /// The consequences of specifying a column index that is out of /// range, are undefined. @@ -217,6 +217,14 @@ class Descriptor : public std::enable_shared_from_this { /// \sa Table::rename_column() void rename_column(size_t col_ndx, StringData new_name); + /// If the descriptor is describing a subtable column, the add_search_index() + /// and remove_search_index() will add or remove search indexes of *all* + /// subtables of the subtable column. This may take a while if there are many + /// subtables with many rows each. + bool has_search_index(size_t column_ndx) const noexcept; + void add_search_index(size_t column_ndx); + void remove_search_index(size_t column_ndx); + /// There are two kinds of links, 'weak' and 'strong'. A strong link is one /// that implies ownership, i.e., that the origin row (parent) owns the /// target row (child). Simply stated, this means that when the origin row @@ -507,11 +515,8 @@ class Descriptor : public std::enable_shared_from_this { // return null. DescriptorRef get_subdesc_accessor(size_t column_ndx) noexcept; - void move_column(size_t from_ndx, size_t to_ndx); - void adj_insert_column(size_t col_ndx) noexcept; void adj_erase_column(size_t col_ndx) noexcept; - void adj_move_column(size_t col_ndx_1, size_t col_ndx_2) noexcept; friend class util::bind_ptr; friend class util::bind_ptr; @@ -643,14 +648,6 @@ inline void Descriptor::rename_column(size_t col_ndx, StringData name) tf::rename_column(*this, col_ndx, name); // Throws } -inline void Descriptor::move_column(size_t from_ndx, size_t to_ndx) -{ - REALM_ASSERT(is_attached()); - typedef _impl::TableFriend tf; - tf::move_column(*this, from_ndx, to_ndx); // Throws - adj_move_column(from_ndx, to_ndx); -} - inline void Descriptor::set_link_type(size_t col_ndx, LinkType link_type) { typedef _impl::TableFriend tf; @@ -764,6 +761,11 @@ class _impl::DescriptorFriend { desc.detach(); } + static void detach_subdesc_accessors(Descriptor& desc) noexcept + { + desc.detach_subdesc_accessors(); + } + static Table& get_root_table(Descriptor& desc) noexcept { return *desc.m_root_table; @@ -794,11 +796,6 @@ class _impl::DescriptorFriend { return desc.get_subdesc_accessor(column_ndx); } - static void move_column(Descriptor& desc, size_t from_ndx, size_t to_ndx) - { - return desc.move_column(from_ndx, to_ndx); - } - static void adj_insert_column(Descriptor& desc, size_t col_ndx) noexcept { desc.adj_insert_column(col_ndx); @@ -808,11 +805,6 @@ class _impl::DescriptorFriend { { desc.adj_erase_column(col_ndx); } - - static void adj_move_column(Descriptor& desc, size_t col_ndx_1, size_t col_ndx_2) noexcept - { - desc.adj_move_column(col_ndx_1, col_ndx_2); - } }; } // namespace realm diff --git a/Pods/Realm/include/core/realm/exceptions.hpp b/Pods/Realm/include/core/realm/exceptions.hpp index a2dcd55..3280a14 100644 --- a/Pods/Realm/include/core/realm/exceptions.hpp +++ b/Pods/Realm/include/core/realm/exceptions.hpp @@ -57,12 +57,12 @@ class DescriptorMismatch : public std::exception { }; -/// The \c FileFormatUpgradeRequired exception can be thrown by the \c -/// SharedGroup constructor when opening a database that uses a deprecated file -/// format, and the user has indicated he does not want automatic upgrades to -/// be performed. This exception indicates that until an upgrade of the file -/// format is performed, the database will be unavailable for read or write -/// operations. +/// The FileFormatUpgradeRequired exception can be thrown by the SharedGroup +/// constructor when opening a database that uses a deprecated file format +/// and/or a deprecated history schema, and the user has indicated he does not +/// want automatic upgrades to be performed. This exception indicates that until +/// an upgrade of the file format is performed, the database will be unavailable +/// for read or write operations. class FileFormatUpgradeRequired : public std::exception { public: const char* what() const noexcept override; @@ -85,6 +85,26 @@ class AddressSpaceExhausted : public std::runtime_error { /// runtime_error::what() returns the msg provided in the constructor. }; +/// Thrown when creating references that are too large to be contained in our ref_type (size_t) +class MaximumFileSizeExceeded : public std::runtime_error { +public: + MaximumFileSizeExceeded(const std::string& msg); + /// runtime_error::what() returns the msg provided in the constructor. +}; + +/// Thrown when writing fails because the disk is full. +class OutOfDiskSpace : public std::runtime_error { +public: + OutOfDiskSpace(const std::string& msg); + /// runtime_error::what() returns the msg provided in the constructor. +}; + + +class SerialisationError : public std::runtime_error { +public: + SerialisationError(const std::string& msg); + /// runtime_error::what() returns the msg provided in the constructor. +}; /// The \c LogicError exception class is intended to be thrown only when /// applications (or bindings) violate rules that are stated (or ought to have @@ -199,8 +219,19 @@ class LogicError : public std::exception { /// session. mixed_history_type, + /// History schema version (as specified by the Replication + /// implementation passed to the SharedGroup constructor) was not + /// consistent across the session. + mixed_history_schema_version, + /// Adding rows to a table with no columns is not supported. - table_has_no_columns + table_has_no_columns, + + /// Referring to a column that has been deleted. + column_does_not_exist, + + /// You can not add index on a subtable of a subtable + subtable_of_subtable_index }; LogicError(ErrorKind message); @@ -254,6 +285,21 @@ inline AddressSpaceExhausted::AddressSpaceExhausted(const std::string& msg) { } +inline MaximumFileSizeExceeded::MaximumFileSizeExceeded(const std::string& msg) + : std::runtime_error(msg) +{ +} + +inline OutOfDiskSpace::OutOfDiskSpace(const std::string& msg) +: std::runtime_error(msg) +{ +} + +inline SerialisationError::SerialisationError(const std::string& msg) +: std::runtime_error(msg) +{ +} + inline LogicError::LogicError(LogicError::ErrorKind k) : m_kind(k) { diff --git a/Pods/Realm/include/core/realm/group.hpp b/Pods/Realm/include/core/realm/group.hpp index 490dd26..98b6d07 100644 --- a/Pods/Realm/include/core/realm/group.hpp +++ b/Pods/Realm/include/core/realm/group.hpp @@ -29,9 +29,9 @@ #include #include #include -#include +#include +#include #include -#include #include namespace realm { @@ -103,6 +103,7 @@ class Group : private Table::Parent { // FIXME: Implement a proper copy constructor (fairly trivial). Group(const Group&) = delete; + Group& operator=(const Group&) = delete; ~Group() noexcept override; @@ -139,6 +140,15 @@ class Group : private Table::Parent { /// change the association between the Group instance and the file /// that was specified in the call to open(). /// + /// A Realm file that contains a history (see Replication::HistoryType) may + /// be opened via Group::open(), as long as the application can ensure that + /// there is no concurrent access to the file (see below for more on + /// concurrency), but if the file is modified via Group::commit() the + /// history will be discarded. To retain the history, the application must + /// instead access the file in shared mode, i.e., via SharedGroup, and + /// supply the right kind of replication plugin (see + /// Replication::get_history_type()). + /// /// A file that is passed to Group::open(), may not be modified by /// a third party until after the Group object is /// destroyed. Behavior is undefined if a file is modified by a @@ -277,8 +287,6 @@ class Group : private Table::Parent { /// the dynamic type (descriptor) to match the statically specified custom /// table type. /// - /// \tparam T An instance of the BasicTable class template. - /// /// \param index Index of table in this group. /// /// \param name Name of table. All strings are valid table names as long as @@ -335,30 +343,6 @@ class Group : private Table::Parent { TableRef get_or_add_table(StringData name, bool* was_added = nullptr); TableRef get_or_insert_table(size_t index, StringData name, bool* was_added = nullptr); - template - BasicTableRef get_table(size_t index); - - template - BasicTableRef get_table(size_t index) const; - - template - BasicTableRef get_table(StringData name); - - template - BasicTableRef get_table(StringData name) const; - - template - BasicTableRef add_table(StringData name, bool require_unique_name = true); - - template - BasicTableRef insert_table(size_t index, StringData name, bool require_unique_name = true); - - template - BasicTableRef get_or_add_table(StringData name, bool* was_added = nullptr); - - template - BasicTableRef get_or_insert_table(size_t index, StringData name, bool* was_added = nullptr); - void remove_table(size_t index); void remove_table(StringData name); @@ -367,17 +351,6 @@ class Group : private Table::Parent { //@} - /// Move the table at \a from_index such that it ends up at \a - /// to_index. Other tables are shifted as necessary in such a way that their - /// order is preserved. - /// - /// Note that \a to_index is the desired final index of the moved table, - /// therefore, `move_table(1,1)` is a no-op, while `move_table(1,2)` moves - /// the table at index 1 by one position, such that it ends up at index 2. A - /// side-effect of that, is that the table, that was originally at index 2, - /// is moved to index 1. - void move_table(size_t from_index, size_t to_index); - // Serialization /// Write this database to the specified output stream. @@ -536,6 +509,23 @@ class Group : private Table::Parent { return !(*this == g); } + /// Control of what to include when computing memory usage + enum SizeAggregateControl { + size_of_state = 1, ///< size of tables, indexes, toplevel array + size_of_history = 2, ///< size of the in-file history compartment + size_of_freelists = 4, ///< size of the freelists + size_of_all = 7 + }; + /// Compute the sum of the sizes in number of bytes of all the array nodes + /// that currently make up this group. When this group represents a snapshot + /// in a Realm file (such as during a read transaction via a SharedGroup + /// instance), this function computes the footprint of that snapshot within + /// the Realm file. + /// + /// If this group accessor is the detached state, this function returns + /// zero. + size_t compute_aggregated_byte_size(SizeAggregateControl ctrl = SizeAggregateControl::size_of_all) const noexcept; + void verify() const; #ifdef REALM_DEBUG void print() const; @@ -553,41 +543,46 @@ class Group : private Table::Parent { private: SlabAlloc m_alloc; + int m_file_format_version; /// `m_top` is the root node (or top array) of the Realm, and has the /// following layout: /// ///
     ///
-    ///   slot  value
-    ///   -----------------------
-    ///   1st   m_table_names
-    ///   2nd   m_tables
-    ///   3rd   Logical file size
-    ///   4th   GroupWriter::m_free_positions (optional)
-    ///   5th   GroupWriter::m_free_lengths   (optional)
-    ///   6th   GroupWriter::m_free_versions  (optional)
-    ///   7th   Transaction number / version  (optional)
-    ///   8th   In-Realm history type         (optional)
-    ///   9th   In-Realm history ref          (optional)
+    ///                                                     Introduced in file
+    ///   Slot  Value                                       format version
+    ///   ---------------------------------------------------------------------
+    ///    1st   m_table_names
+    ///    2nd   m_tables
+    ///    3rd   Logical file size
+    ///    4th   GroupWriter::m_free_positions (optional)
+    ///    5th   GroupWriter::m_free_lengths   (optional)
+    ///    6th   GroupWriter::m_free_versions  (optional)
+    ///    7th   Transaction number / version  (optional)
+    ///    8th   History type         (optional)             4
+    ///    9th   History ref          (optional)             4
+    ///   10th   History version      (optional)             7
     ///
     /// 
/// - /// The 'in-Realm history type' slot stores a value of - /// Replication::HistoryType, although never - /// Replication::hist_OutOfRealm. For more information about that, see - /// Replication::get_history_type(). + /// The 'History type' slot stores a value of type + /// Replication::HistoryType. The 'History version' slot stores a history + /// schema version as returned by Replication::get_history_schema_version(). /// /// The first three entries are mandatory. In files created by /// Group::write(), none of the optional entries are present and the size of /// `m_top` is 3. In files updated by Group::commit(), the 4th and 5th entry - /// is present, and the size of `m_top` is 5. In files updated by way of a - /// transaction (SharedGroup::commit()), the 4th, 5th, 6th, and 7th entry is - /// present, and the size of `m_top` is 7. In files that contain a changeset - /// history, the 8th and 9th entry is present. + /// are present, and the size of `m_top` is 5. In files updated by way of a + /// transaction (SharedGroup::commit()), the 4th, 5th, 6th, and 7th entry + /// are present, and the size of `m_top` is 7. In files that contain a + /// changeset history, the 8th, 9th, and 10th entry are present, except that + /// if the file was opened in nonshared mode (via Group::open()), and the + /// file format remains at 6 (not previously upgraded to 7 or later), then + /// the 10th entry will be absent. /// /// When a group accessor is attached to a newly created file or an empty /// memory buffer where there is no top array yet, `m_top`, `m_tables`, and - /// `m_table_names` with be left in the detached state until the initiation + /// `m_table_names` will be left in the detached state until the initiation /// of the first write transaction. In particular, they will remain in the /// detached state during read transactions that precede the first write /// transaction. @@ -603,6 +598,8 @@ class Group : private Table::Parent { std::function m_notify_handler; std::function m_schema_change_handler; + std::shared_ptr m_metrics; + size_t m_total_rows; struct shared_tag { }; @@ -610,6 +607,8 @@ class Group : private Table::Parent { void init_array_parents() noexcept; + void open(ref_type top_ref, const std::string& file_path); + /// If `top_ref` is not zero, attach this group accessor to the specified /// underlying node structure. If `top_ref` is zero and \a /// create_group_when_missing is true, create a new node structure that @@ -654,14 +653,18 @@ class Group : private Table::Parent { // Overriding method in Table::Parent void child_accessor_destroyed(Table*) noexcept override; + // Overriding method in Table::Parent + std::recursive_mutex* get_accessor_management_lock() noexcept override + { return nullptr; } // we don't need locking for group! + // Overriding method in Table::Parent Group* get_parent_group() noexcept override; class TableWriter; class DefaultTableWriter; - static void write(std::ostream&, const Allocator&, TableWriter&, bool no_top_array, bool pad_for_encryption, - uint_fast64_t version_number); + static void write(std::ostream&, int file_format_version, TableWriter&, bool no_top_array, + bool pad_for_encryption, uint_fast64_t version_number); typedef void (*DescSetter)(Table&); typedef bool (*DescMatcher)(const Spec&); @@ -689,12 +692,80 @@ class Group : private Table::Parent { Replication* get_replication() const noexcept; void set_replication(Replication*) noexcept; + std::shared_ptr get_metrics() const noexcept; + void set_metrics(std::shared_ptr other) noexcept; + void update_num_objects(); class TransactAdvancer; void advance_transact(ref_type new_top_ref, size_t new_file_size, _impl::NoCopyInputStream&); void refresh_dirty_accessors(); template void update_table_indices(F&& map_function); + /// \brief The version of the format of the node structure (in file or in + /// memory) in use by Realm objects associated with this group. + /// + /// Every group contains a file format version field, which is returned + /// by this function. The file format version field is set to the file format + /// version specified by the attached file (or attached memory buffer) at the + /// time of attachment and the value is used to determine if a file format + /// upgrade is required. + /// + /// A value of zero means that the file format is not yet decided. This is + /// only possible for empty Realms where top-ref is zero. (When group is created + /// with the unattached_tag). The version number will then be determined in the + /// subsequent call to Group::open. + /// + /// In shared mode (when a Realm file is opened via a SharedGroup instance) + /// it can happen that the file format is upgraded asyncronously (via + /// another SharedGroup instance), and in that case the file format version + /// field can get out of date, but only for a short while. It is always + /// guaranteed to be, and remain up to date after the opening process completes + /// (when SharedGroup::do_open() returns). + /// + /// An empty Realm file (one whose top-ref is zero) may specify a file + /// format version of zero to indicate that the format is not yet + /// decided. In that case the file format version must be changed to a proper + /// before the opening process completes (Group::open() or SharedGroup::open()). + /// + /// File format versions: + /// + /// 1 Initial file format version + /// + /// 2 Various changes. + /// + /// 3 Supporting null on string columns broke the file format in following + /// way: Index appends an 'X' character to all strings except the null + /// string, to be able to distinguish between null and empty + /// string. Bumped to 3 because of null support of String columns and + /// because of new format of index. + /// + /// 4 Introduction of optional in-Realm history of changes (additional + /// entries in Group::m_top). Since this change is not forward + /// compatible, the file format version had to be bumped. This change is + /// implemented in a way that achieves backwards compatibility with + /// version 3 (and in turn with version 2). + /// + /// 5 Introduced the new Timestamp column type that replaces DateTime. + /// When opening an older database file, all DateTime columns will be + /// automatically upgraded Timestamp columns. + /// + /// 6 Introduced a new structure for the StringIndex. Moved the commit + /// logs into the Realm file. Changes to the transaction log format + /// including reshuffling instructions. This is the format used in + /// milestone 2.0.0. + /// + /// 7 Introduced "history schema version" as 10th entry in top array. + /// + /// 8 Subtables can now have search index. + /// + /// 9 Replication instruction values shuffled, instr_MoveRow added. + /// + /// IMPORTANT: When introducing a new file format version, be sure to review + /// the file validity checks in Group::open() and SharedGroup::do_open, the file + /// format selection logic in + /// Group::get_target_file_format_version_for_session(), and the file format + /// upgrade logic in Group::upgrade_file_format(). + int get_file_format_version() const noexcept; void set_file_format_version(int) noexcept; int get_committed_file_format_version() const noexcept; @@ -710,11 +781,13 @@ class Group : private Table::Parent { void send_cascade_notification(const CascadeNotification& notification) const; void send_schema_change_notification() const; - static void get_version_and_history_type(const Array& top, _impl::History::version_type& version, - int& history_type) noexcept; + static void get_version_and_history_info(const Array& top, _impl::History::version_type& version, + int& history_type, int& history_schema_version) noexcept; static ref_type get_history_ref(const Array& top) noexcept; + static int get_history_schema_version(const Array& top) noexcept; + void set_history_schema_version(int version); void set_history_parent(Array& history_root) noexcept; - void prepare_history_parent(Array& history_root, int history_type); + void prepare_history_parent(Array& history_root, int history_type, int history_schema_version); friend class Table; friend class GroupWriter; @@ -724,6 +797,8 @@ class Group : private Table::Parent { friend class _impl::TransactLogParser; friend class Replication; friend class TrivialReplication; + friend class metrics::QueryInfo; + friend class metrics::Metrics; }; @@ -735,6 +810,7 @@ inline Group::Group(const std::string& file, const char* key, OpenMode mode) , m_tables(m_alloc) , m_table_names(m_alloc) , m_is_shared(false) + , m_total_rows(0) { init_array_parents(); @@ -747,6 +823,7 @@ inline Group::Group(BinaryData buffer, bool take_ownership) , m_tables(m_alloc) , m_table_names(m_alloc) , m_is_shared(false) + , m_total_rows(0) { init_array_parents(); open(buffer, take_ownership); // Throws @@ -759,6 +836,7 @@ inline Group::Group(unattached_tag) noexcept , m_tables(m_alloc) , m_table_names(m_alloc) , m_is_shared(false) + , m_total_rows(0) { init_array_parents(); } @@ -775,6 +853,7 @@ inline Group::Group(shared_tag) noexcept , m_tables(m_alloc) , m_table_names(m_alloc) , m_is_shared(true) + , m_total_rows(0) { init_array_parents(); } @@ -894,91 +973,6 @@ inline TableRef Group::get_or_add_table(StringData name, bool* was_added) return TableRef(table); } -template -inline BasicTableRef Group::get_table(size_t table_ndx) -{ - static_assert(IsBasicTable::value, "Invalid table type"); - if (!is_attached()) - throw LogicError(LogicError::detached_accessor); - DescMatcher desc_matcher = &T::matches_dynamic_type; - Table* table = do_get_table(table_ndx, desc_matcher); // Throws - return BasicTableRef(static_cast(table)); -} - -template -inline BasicTableRef Group::get_table(size_t table_ndx) const -{ - static_assert(IsBasicTable::value, "Invalid table type"); - if (!is_attached()) - throw LogicError(LogicError::detached_accessor); - DescMatcher desc_matcher = &T::matches_dynamic_type; - const Table* table = do_get_table(table_ndx, desc_matcher); // Throws - return BasicTableRef(static_cast(table)); -} - -template -inline BasicTableRef Group::get_table(StringData name) -{ - static_assert(IsBasicTable::value, "Invalid table type"); - if (!is_attached()) - throw LogicError(LogicError::detached_accessor); - DescMatcher desc_matcher = &T::matches_dynamic_type; - Table* table = do_get_table(name, desc_matcher); // Throws - return BasicTableRef(static_cast(table)); -} - -template -inline BasicTableRef Group::get_table(StringData name) const -{ - static_assert(IsBasicTable::value, "Invalid table type"); - if (!is_attached()) - throw LogicError(LogicError::detached_accessor); - DescMatcher desc_matcher = &T::matches_dynamic_type; - const Table* table = do_get_table(name, desc_matcher); // Throws - return BasicTableRef(static_cast(table)); -} - -template -inline BasicTableRef Group::insert_table(size_t table_ndx, StringData name, bool require_unique_name) -{ - static_assert(IsBasicTable::value, "Invalid table type"); - if (!is_attached()) - throw LogicError(LogicError::detached_accessor); - DescSetter desc_setter = &T::set_dynamic_type; - Table* table = do_insert_table(table_ndx, name, desc_setter, require_unique_name); // Throws - return BasicTableRef(static_cast(table)); -} - -template -inline BasicTableRef Group::add_table(StringData name, bool require_unique_name) -{ - return insert_table(size(), name, require_unique_name); -} - -template -BasicTableRef Group::get_or_insert_table(size_t table_ndx, StringData name, bool* was_added) -{ - static_assert(IsBasicTable::value, "Invalid table type"); - if (!is_attached()) - throw LogicError(LogicError::detached_accessor); - DescMatcher desc_matcher = &T::matches_dynamic_type; - DescSetter desc_setter = &T::set_dynamic_type; - Table* table = do_get_or_insert_table(table_ndx, name, desc_matcher, desc_setter, was_added); // Throws - return BasicTableRef(static_cast(table)); -} - -template -BasicTableRef Group::get_or_add_table(StringData name, bool* was_added) -{ - static_assert(IsBasicTable::value, "Invalid table type"); - if (!is_attached()) - throw LogicError(LogicError::detached_accessor); - DescMatcher desc_matcher = &T::matches_dynamic_type; - DescSetter desc_setter = &T::set_dynamic_type; - Table* table = do_get_or_add_table(name, desc_matcher, desc_setter, was_added); // Throws - return BasicTableRef(static_cast(table)); -} - template void Group::to_json(S& out, size_t link_depth, std::map* renames) const { @@ -1067,19 +1061,24 @@ inline void Group::send_schema_change_notification() const m_schema_change_handler(); } -inline void Group::get_version_and_history_type(const Array& top, _impl::History::version_type& version, - int& history_type) noexcept +inline void Group::get_version_and_history_info(const Array& top, _impl::History::version_type& version, + int& history_type, int& history_schema_version) noexcept { - _impl::History::version_type version_2 = 0; + using version_type = _impl::History::version_type; + version_type version_2 = 0; int history_type_2 = 0; + int history_schema_version_2 = 0; if (top.is_attached()) { if (top.size() >= 6) { REALM_ASSERT(top.size() >= 7); - version_2 = _impl::History::version_type(top.get(6) / 2); + version_2 = version_type(top.get_as_ref_or_tagged(6).get_as_int()); } if (top.size() >= 8) { REALM_ASSERT(top.size() >= 9); - history_type_2 = int(top.get(7) / 2); + history_type_2 = int(top.get_as_ref_or_tagged(7).get_as_int()); + } + if (top.size() >= 10) { + history_schema_version_2 = int(top.get_as_ref_or_tagged(9).get_as_int()); } } // Version 0 is not a legal initial version, so it has to be set to 1 @@ -1088,39 +1087,54 @@ inline void Group::get_version_and_history_type(const Array& top, _impl::History version_2 = 1; version = version_2; history_type = history_type_2; + history_schema_version = history_schema_version_2; } inline ref_type Group::get_history_ref(const Array& top) noexcept { - if (top.is_attached()) { - if (top.size() >= 8) { - REALM_ASSERT(top.size() >= 9); - return top.get_as_ref(8); - } + bool has_history = (top.is_attached() && top.size() >= 8); + if (has_history) { + // This function is only used is shared mode (from SharedGroup) + REALM_ASSERT(top.size() >= 10); + return top.get_as_ref(8); } return 0; } -inline void Group::set_history_parent(Array& history_root) noexcept +inline int Group::get_history_schema_version(const Array& top) noexcept { - history_root.set_parent(&m_top, 8); + bool has_history = (top.is_attached() && top.size() >= 8); + if (has_history) { + // This function is only used is shared mode (from SharedGroup) + REALM_ASSERT(top.size() >= 10); + return int(top.get_as_ref_or_tagged(9).get_as_int()); + } + return 0; +} + +inline void Group::set_history_schema_version(int version) +{ + // This function is only used is shared mode (from SharedGroup) + REALM_ASSERT(m_top.size() >= 10); + m_top.set(9, RefOrTagged::make_tagged(unsigned(version))); // Throws } -inline void Group::prepare_history_parent(Array& history_root, int history_type) +inline void Group::set_history_parent(Array& history_root) noexcept { - REALM_ASSERT(m_alloc.get_file_format_version() >= 4); - // Ensure that there are slots for both the history type and the history - // ref. - while (m_top.size() < 9) - m_top.add(0); // Throws - m_top.set(7, RefOrTagged::make_tagged(history_type)); // Throws - set_history_parent(history_root); + history_root.set_parent(&m_top, 8); } class Group::TableWriter { public: + struct HistoryInfo { + ref_type ref = 0; + int type = 0; + int version = 0; + }; + virtual ref_type write_names(_impl::OutputStream&) = 0; virtual ref_type write_tables(_impl::OutputStream&) = 0; + virtual HistoryInfo write_history(_impl::OutputStream&) = 0; virtual ~TableWriter() noexcept { } @@ -1151,6 +1165,16 @@ inline void Group::set_replication(Replication* repl) noexcept m_alloc.set_replication(repl); } +inline std::shared_ptr Group::get_metrics() const noexcept +{ + return m_metrics; +} + +inline void Group::set_metrics(std::shared_ptr shared) noexcept +{ + m_metrics = shared; +} + // The purpose of this class is to give internal access to some, but // not all of the non-public parts of the Group class. class _impl::GroupFriend { @@ -1160,6 +1184,16 @@ class _impl::GroupFriend { return group.m_alloc; } + static const Allocator& get_alloc(const Group& group) noexcept + { + return group.m_alloc; + } + + static ref_type get_top_ref(const Group& group) noexcept + { + return group.m_top.get_ref(); + } + static Table& get_table(Group& group, size_t ndx_in_group) { Group::DescMatcher desc_matcher = 0; // Do not check descriptor @@ -1265,13 +1299,15 @@ class _impl::GroupFriend { group.create_empty_group(); // Throws } - static void get_version_and_history_type(Allocator& alloc, ref_type top_ref, - _impl::History::version_type& version, int& history_type) noexcept + static void get_version_and_history_info(const Allocator& alloc, ref_type top_ref, + _impl::History::version_type& version, + int& history_type, + int& history_schema_version) noexcept { - Array top(alloc); + Array top{const_cast(alloc)}; if (top_ref != 0) top.init_from_ref(top_ref); - Group::get_version_and_history_type(top, version, history_type); + Group::get_version_and_history_info(top, version, history_type, history_schema_version); } static ref_type get_history_ref(const Group& group) noexcept @@ -1287,14 +1323,33 @@ class _impl::GroupFriend { return Group::get_history_ref(top); } + static int get_history_schema_version(const Group& group) noexcept + { + return Group::get_history_schema_version(group.m_top); + } + + static int get_history_schema_version(Allocator& alloc, ref_type top_ref) noexcept + { + Array top{alloc}; + if (top_ref != 0) + top.init_from_ref(top_ref); + return Group::get_history_schema_version(top); + } + + static void set_history_schema_version(Group& group, int version) + { + group.set_history_schema_version(version); // Throws + } + static void set_history_parent(Group& group, Array& history_root) noexcept { group.set_history_parent(history_root); } - static void prepare_history_parent(Group& group, Array& history_root, int history_type) + static void prepare_history_parent(Group& group, Array& history_root, int history_type, + int history_schema_version) { - group.prepare_history_parent(history_root, history_type); // Throws + group.prepare_history_parent(history_root, history_type, history_schema_version); // Throws } static int get_file_format_version(const Group& group) noexcept @@ -1349,6 +1404,9 @@ struct CascadeState : Group::CascadeNotification { /// If false, the links field is not needed, so any work done just for that /// can be skipped. bool track_link_nullifications = false; + + /// If false, weak links are followed too + bool only_strong_links = true; }; inline bool Group::CascadeNotification::row::operator==(const row& r) const noexcept diff --git a/Pods/Realm/include/core/realm/group_shared.hpp b/Pods/Realm/include/core/realm/group_shared.hpp index 8920ff7..36b7d5e 100644 --- a/Pods/Realm/include/core/realm/group_shared.hpp +++ b/Pods/Realm/include/core/realm/group_shared.hpp @@ -19,22 +19,17 @@ #ifndef REALM_GROUP_SHARED_HPP #define REALM_GROUP_SHARED_HPP -#ifdef REALM_DEBUG -#include // usleep() -#endif - #include #include #include #include -#ifndef _WIN32 #include -#endif #include #include #include #include #include +#include #include #include @@ -54,9 +49,15 @@ struct IncompatibleLockFile : std::runtime_error { } }; -/// Thrown by SharedGroup::open() if the realm database was generated with -/// a format for Realm Mobile Platform but is being opened as a Realm -/// Mobile Database or vice versa. +/// Thrown by SharedGroup::open() if the type of history +/// (Replication::HistoryType) in the opened Realm file is incompatible with the +/// mode in which the Realm file is opened. For example, if there is a mismatch +/// between the history type in the file, and the history type associated with +/// the replication plugin passed to SharedGroup::open(). +/// +/// This exception will also be thrown if the history schema version is lower +/// than required, and no migration is possible +/// (Replication::is_upgradable_history_schema()). struct IncompatibleHistories : util::File::AccessError { IncompatibleHistories(const std::string& msg, const std::string& path) : util::File::AccessError("Incompatible histories. " + msg, path) @@ -171,6 +172,11 @@ class SharedGroup { ~SharedGroup() noexcept; + // Disable copying to prevent accessor errors. If you really want another + // instance, open another SharedGroup object on the same file. + SharedGroup(const SharedGroup&) = delete; + SharedGroup& operator=(const SharedGroup&) = delete; + /// Attach this SharedGroup instance to the specified database file. /// /// While at least one instance of SharedGroup exists for a specific @@ -217,6 +223,9 @@ class SharedGroup { /// on an unattached instance results in undefined behavior. bool is_attached() const noexcept; +#ifdef REALM_DEBUG + /// Deprecated method, only called from a unit test + /// /// Reserve disk space now to avoid allocation errors at a later /// point in time, and to minimize on-disk fragmentation. In some /// cases, less fragmentation translates into improved @@ -229,14 +238,12 @@ class SharedGroup { /// default). If the file is already bigger than the specified /// size, the size will be unchanged, and on-disk allocation will /// occur only for the initial section that corresponds to the - /// specified size. On systems that do not support preallocation, - /// this function has no effect. To know whether preallocation is - /// supported by Realm on your platform, call - /// util::File::is_prealloc_supported(). + /// specified size. /// /// It is an error to call this function on an unattached shared /// group. Doing so will result in undefined behavior. void reserve(size_t size_in_bytes); +#endif /// Querying for changes: /// @@ -350,6 +357,11 @@ class SharedGroup { const Group& begin_read(VersionID version = VersionID()); void end_read() noexcept; Group& begin_write(); + // Return true (and take the write lock) if there is no other write + // in progress. In case of contention return false immediately. + // If the write lock is obtained, also provide the Group associated + // with the SharedGroup for further operations. + bool try_begin_write(Group*& group); version_type commit(); void rollback() noexcept; // report statistics of last commit done on THIS shared group. @@ -516,6 +528,27 @@ class SharedGroup { // Release pinned version (not thread safe) void unpin_version(VersionID version); +#if REALM_METRICS + std::shared_ptr get_metrics(); +#endif // REALM_METRICS + + // Try to grab a exclusive lock of the given realm path's lock file. If the lock + // can be acquired, the callback will be executed with the lock and then return true. + // Otherwise false will be returned directly. + // The lock taken precludes races with other threads or processes accessing the + // files through a SharedGroup. + // It is safe to delete/replace realm files inside the callback. + // WARNING: It is not safe to delete the lock file in the callback. + using CallbackWithLock = std::function; + static bool call_with_lock(const std::string& realm_path, CallbackWithLock callback); + + // Return a list of files/directories core may use of the given realm file path. + // The first element of the pair in the returned list is the path string, the + // second one is to indicate the path is a directory or not. + // The temporary files are not returned by this function. + // It is safe to delete those returned files/directories in the call_with_lock's callback. + static std::vector> get_core_files(const std::string& realm_path); + private: struct SharedInfo; struct ReadCount; @@ -548,16 +581,19 @@ class SharedGroup { util::InterprocessMutex m_balancemutex; #endif util::InterprocessMutex m_controlmutex; -#ifndef _WIN32 #ifdef REALM_ASYNC_DAEMON util::InterprocessCondVar m_room_to_write; util::InterprocessCondVar m_work_to_do; util::InterprocessCondVar m_daemon_becomes_ready; #endif util::InterprocessCondVar m_new_commit_available; -#endif + util::InterprocessCondVar m_pick_next_writer; std::function m_upgrade_callback; +#if REALM_METRICS + std::shared_ptr m_metrics; +#endif // REALM_METRICS + void do_open(const std::string& file, bool no_create, bool is_backend, const SharedGroupOptions options); // Ring buffer management @@ -593,9 +629,12 @@ class SharedGroup { void do_begin_read(VersionID, bool writable); void do_end_read() noexcept; + /// return true if write transaction can commence, false otherwise. + bool do_try_begin_write(); void do_begin_write(); version_type do_commit(); void do_end_write() noexcept; + void set_transact_stage(TransactStage stage) noexcept; /// Returns the version of the latest snapshot. version_type get_version_of_latest_snapshot(); @@ -615,7 +654,9 @@ class SharedGroup { void do_async_commits(); - void upgrade_file_format(bool allow_file_format_upgrade, int target_file_format_version); + /// Upgrade file format and/or history schema + void upgrade_file_format(bool allow_file_format_upgrade, int target_file_format_version, + int current_hist_schema_version, int target_hist_schema_version); //@{ /// See LangBindHelper. @@ -640,6 +681,10 @@ class SharedGroup { int get_file_format_version() const noexcept; + /// finish up the process of starting a write transaction. Internal use only. + void finish_begin_write(); + + void close_internal(std::unique_lock) noexcept; friend class _impl::SharedGroupFriend; }; @@ -678,12 +723,6 @@ class ReadTransaction { return get_group().get_table(name); // Throws } - template - BasicTableRef get_table(StringData name) const - { - return get_group().get_table(name); // Throws - } - const Group& get_group() const noexcept; /// Get the version of the snapshot to which this read transaction is bound. @@ -733,24 +772,6 @@ class WriteTransaction { return get_group().get_or_add_table(name, was_added); // Throws } - template - BasicTableRef get_table(StringData name) const - { - return get_group().get_table(name); // Throws - } - - template - BasicTableRef add_table(StringData name, bool require_unique_name = true) const - { - return get_group().add_table(name, require_unique_name); // Throws - } - - template - BasicTableRef get_or_add_table(StringData name, bool* was_added = nullptr) const - { - return get_group().get_or_add_table(name, was_added); // Throws - } - Group& get_group() const noexcept; /// Get the version of the snapshot on which this write transaction is @@ -975,7 +996,7 @@ inline void SharedGroup::promote_to_write(O* observer) throw; } - m_transact_stage = transact_Writing; + set_transact_stage(transact_Writing); } template @@ -988,10 +1009,6 @@ inline void SharedGroup::rollback_and_continue_as_read(O* observer) if (!hist) throw LogicError(LogicError::no_history); - // Mark all managed space (beyond the attached file) as free. - using gf = _impl::GroupFriend; - gf::reset_free_space_tracking(m_group); // Throws - BinaryData uncommitted_changes = hist->get_uncommitted_changes(); // FIXME: We are currently creating two transaction log parsers, one here, @@ -1008,6 +1025,10 @@ inline void SharedGroup::rollback_and_continue_as_read(O* observer) observer->parse_complete(); // Throws } + // Mark all managed space (beyond the attached file) as free. + using gf = _impl::GroupFriend; + gf::reset_free_space_tracking(m_group); // Throws + ref_type top_ref = m_read_lock.m_top_ref; size_t file_size = m_read_lock.m_file_size; _impl::ReversedNoCopyInputStream reversed_in(reverser); @@ -1019,7 +1040,7 @@ inline void SharedGroup::rollback_and_continue_as_read(O* observer) REALM_ASSERT(repl); // Presence of `repl` follows from the presence of `hist` repl->abort_transact(); - m_transact_stage = transact_Reading; + set_transact_stage(transact_Reading); } template diff --git a/Pods/Realm/include/core/realm/group_shared_options.hpp b/Pods/Realm/include/core/realm/group_shared_options.hpp index 8430c44..75d5bda 100644 --- a/Pods/Realm/include/core/realm/group_shared_options.hpp +++ b/Pods/Realm/include/core/realm/group_shared_options.hpp @@ -37,12 +37,15 @@ struct SharedGroupOptions { explicit SharedGroupOptions(Durability level = Durability::Full, const char* key = nullptr, bool allow_upgrade = true, std::function file_upgrade_callback = std::function(), - std::string temp_directory = sys_tmp_dir) + std::string temp_directory = sys_tmp_dir, + bool track_metrics = false) : durability(level) , encryption_key(key) , allow_file_format_upgrade(allow_upgrade) , upgrade_callback(file_upgrade_callback) , temp_dir(temp_directory) + , enable_metrics(track_metrics) + { } @@ -52,6 +55,7 @@ struct SharedGroupOptions { , allow_file_format_upgrade(true) , upgrade_callback(std::function()) , temp_dir(sys_tmp_dir) + , enable_metrics(false) { } @@ -87,8 +91,19 @@ struct SharedGroupOptions { /// This string should include a trailing slash '/'. std::string temp_dir; + /// Controls the feature of collecting various metrics to the SharedGroup. + /// A prerequisite is compiling with REALM_METRICS=ON. + bool enable_metrics; + + /// sys_tmp_dir will be used if the temp_dir is empty when creating SharedGroupOptions. + /// It must be writable and allowed to create pipe/fifo file on it. + /// set_sys_tmp_dir is not a thread-safe call and it is only supposed to be called once + // when process starts. + static void set_sys_tmp_dir(const std::string& dir) noexcept { sys_tmp_dir = dir; } + static std::string get_sys_tmp_dir() noexcept { return sys_tmp_dir; } + private: - const static std::string sys_tmp_dir; + static std::string sys_tmp_dir; }; } // end namespace realm diff --git a/Pods/Realm/include/core/realm/group_writer.hpp b/Pods/Realm/include/core/realm/group_writer.hpp index 5067f18..6160105 100644 --- a/Pods/Realm/include/core/realm/group_writer.hpp +++ b/Pods/Realm/include/core/realm/group_writer.hpp @@ -21,6 +21,7 @@ #include // unint8_t etc #include +#include #include #include @@ -87,7 +88,29 @@ class GroupWriter : public _impl::ArrayWriterBase { ArrayInteger m_free_versions; // 6th slot in Group::m_top uint64_t m_current_version; uint64_t m_readlock_version; - + size_t m_window_alignment; + + struct FreeSpaceEntry { + FreeSpaceEntry(size_t r, size_t s, uint64_t v) + : ref(r) + , size(s) + , released_at_version(v) + { + } + size_t ref; + size_t size; + uint64_t released_at_version; + }; + std::vector m_free_in_file; + std::vector m_not_free_in_file; + std::multimap m_size_map; + using FreeListElement = std::multimap::iterator; + + void sort_freelist(); + // Merge adjacent chunks + void merge_adjacent_entries_in_freelist(); + void read_in_freelist(); + size_t recreate_freelist(size_t reserve_pos); // Currently cached memory mappings. We keep as many as 16 1MB windows // open for writing. The allocator will favor sequential allocation // from a modest number of windows, depending upon fragmentation, so @@ -105,8 +128,8 @@ class GroupWriter : public _impl::ArrayWriterBase { // Sync all cached memory mappings void sync_all_mappings(); - // Merge adjacent chunks - void merge_free_space(); + // Copy free space entries to structure where entries are sorted by size + void move_free_in_file_to_size_map(); /// Allocate a chunk of free space of the specified size. The /// specified size must be 8-byte aligned. Extend the file if @@ -128,13 +151,14 @@ class GroupWriter : public _impl::ArrayWriterBase { /// \return A pair (`chunk_ndx`, `chunk_size`) where `chunk_ndx` /// is the index of a chunk whose size is at least the requestd /// size, and `chunk_size` is the size of that chunk. - std::pair reserve_free_space(size_t size); + FreeListElement reserve_free_space(size_t size); + + FreeListElement search_free_space_in_free_list_element(FreeListElement element, size_t size); /// Search only a range of the free list for a block as big as the /// specified size. Return a pair with index and size of the found chunk. /// \param found indicates whether a suitable block was found. - std::pair search_free_space_in_part_of_freelist(size_t size, size_t begin, size_t end, - bool& found); + FreeListElement search_free_space_in_part_of_freelist(size_t size); /// Extend the file to ensure that a chunk of free space of the /// specified size is available. The specified size does not need @@ -144,10 +168,10 @@ class GroupWriter : public _impl::ArrayWriterBase { /// \return A pair (`chunk_ndx`, `chunk_size`) where `chunk_ndx` /// is the index of a chunk whose size is at least the requestd /// size, and `chunk_size` is the size of that chunk. - std::pair extend_free_space(size_t requested_size); + FreeListElement extend_free_space(size_t requested_size); void write_array_at(MapWindow* window, ref_type, const char* data, size_t size); - size_t split_freelist_chunk(size_t index, size_t start_pos, size_t alloc_pos, size_t chunk_size, bool is_shared); + FreeListElement split_freelist_chunk(FreeListElement, size_t alloc_pos); }; diff --git a/Pods/Realm/include/core/realm/handover_defs.hpp b/Pods/Realm/include/core/realm/handover_defs.hpp index 6950bd5..ee554ff 100644 --- a/Pods/Realm/include/core/realm/handover_defs.hpp +++ b/Pods/Realm/include/core/realm/handover_defs.hpp @@ -31,7 +31,10 @@ struct RowBaseHandoverPatch; struct TableViewHandoverPatch; struct TableHandoverPatch { + bool m_is_sub_table; size_t m_table_num; + size_t m_col_ndx; + size_t m_row_ndx; }; struct LinkViewHandoverPatch { @@ -54,9 +57,9 @@ struct QueryHandoverPatch { QueryNodeHandoverPatches m_node_data; }; -struct SortDescriptorHandoverPatch { - std::vector> columns; - std::vector ascending; +struct DescriptorOrderingHandoverPatch { + std::vector>> columns; + std::vector> ascending; }; struct TableViewHandoverPatch { @@ -66,8 +69,7 @@ struct TableViewHandoverPatch { bool was_in_sync; QueryHandoverPatch query_patch; std::unique_ptr linkview_patch; - std::unique_ptr sort_patch; - std::unique_ptr distinct_patch; + std::unique_ptr descriptors_patch; }; diff --git a/Pods/Realm/include/core/realm/impl/clamped_hex_dump.hpp b/Pods/Realm/include/core/realm/impl/clamped_hex_dump.hpp new file mode 100644 index 0000000..353e546 --- /dev/null +++ b/Pods/Realm/include/core/realm/impl/clamped_hex_dump.hpp @@ -0,0 +1,49 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2015] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ + +#ifndef REALM_IMPL_CLAMPED_HEX_DUMP_HPP +#define REALM_IMPL_CLAMPED_HEX_DUMP_HPP + +#include +#include + +namespace realm { +namespace _impl { + +/// Limit the amount of dumped data to 1024 bytes. For use in connection with +/// logging. +inline std::string clamped_hex_dump(BinaryData blob, std::size_t max_size = 1024) +{ + bool was_clipped = false; + std::size_t size_2 = blob.size(); + if (size_2 > max_size) { + size_2 = max_size; + was_clipped = true; + } + std::string str = util::hex_dump(blob.data(), size_2); // Throws + if (was_clipped) + str += "..."; // Throws + return str; +} + +} // namespace _impl +} // namespace realm + +#endif // REALM_IMPL_CLAMPED_HEX_DUMP_HPP diff --git a/Pods/Realm/include/core/realm/impl/clock.hpp b/Pods/Realm/include/core/realm/impl/clock.hpp new file mode 100644 index 0000000..fdf8aa3 --- /dev/null +++ b/Pods/Realm/include/core/realm/impl/clock.hpp @@ -0,0 +1,54 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2015] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ + +#ifndef REALM_IMPL_CLOCK_HPP +#define REALM_IMPL_CLOCK_HPP + +#include +#include + +#include + +namespace realm { +namespace _impl { + +inline sync::milliseconds_type realtime_clock_now() noexcept +{ + using clock = std::chrono::system_clock; + auto time_since_epoch = clock::now().time_since_epoch(); + auto millis_since_epoch = + std::chrono::duration_cast(time_since_epoch).count(); + return sync::milliseconds_type(millis_since_epoch); +} + + +inline sync::milliseconds_type monotonic_clock_now() noexcept +{ + using clock = std::chrono::steady_clock; + auto time_since_epoch = clock::now().time_since_epoch(); + auto millis_since_epoch = + std::chrono::duration_cast(time_since_epoch).count(); + return sync::milliseconds_type(millis_since_epoch); +} + +} // namespace _impl +} // namespace realm + +#endif // REALM_IMPL_CLOCK_HPP diff --git a/Pods/Realm/include/core/realm/impl/continuous_transactions_history.hpp b/Pods/Realm/include/core/realm/impl/cont_transact_hist.hpp similarity index 72% rename from Pods/Realm/include/core/realm/impl/continuous_transactions_history.hpp rename to Pods/Realm/include/core/realm/impl/cont_transact_hist.hpp index c9e43bb..54e0c84 100644 --- a/Pods/Realm/include/core/realm/impl/continuous_transactions_history.hpp +++ b/Pods/Realm/include/core/realm/impl/cont_transact_hist.hpp @@ -16,8 +16,8 @@ * **************************************************************************/ -#ifndef REALM_IMPL_CONTINUOUS_TRANSACTIONS_HISTORY_HPP -#define REALM_IMPL_CONTINUOUS_TRANSACTIONS_HISTORY_HPP +#ifndef REALM_IMPL_CONT_TRANSACT_HIST_HPP +#define REALM_IMPL_CONT_TRANSACT_HIST_HPP #include #include @@ -147,63 +147,6 @@ class History { } }; - -/// This class is intended to eventually become a basis for implementing the -/// Replication API for the purpose of supporting continuous transactions. That -/// is, its purpose is to replace the current implementation in commit_log.cpp, -/// which places the history in separate files. -/// -/// By ensuring that the root node of the history is correctly configured with -/// Group::m_top as its parent, this class allows for modifications of the -/// history as long as those modifications happen after the remainder of the -/// Group accessor is updated to reflect the new snapshot (see -/// History::update_early_from_top_ref()). -class InRealmHistory : public History { -public: - void initialize(Group&); - - /// Must never be called more than once per transaction. Returns the version - /// produced by the added changeset. - version_type add_changeset(BinaryData); - - void update_early_from_top_ref(version_type, size_t, ref_type) override; - void update_from_parent(version_type) override; - void get_changesets(version_type, version_type, BinaryIterator*) const noexcept override; - void set_oldest_bound_version(version_type) override; - - void verify() const override; - -private: - Group* m_group = nullptr; - - /// Version on which the first changeset in the history is based, or if the - /// history is empty, the version associatede with currently bound - /// snapshot. In general, the version associatede with currently bound - /// snapshot is equal to `m_base_version + m_size`, but after - /// add_changeset() is called, it is equal to one minus that. - version_type m_base_version; - - /// Current number of entries in the history. A cache of - /// `m_changesets->size()`. - size_t m_size; - - /// A list of changesets, one for each entry in the history. If null, the - /// history is empty. - /// - /// FIXME: Ideally, the B+tree accessor below should have been just - /// Bptree, but Bptree seems to not allow that yet. - /// - /// FIXME: The memory-wise indirection is an unfortunate consequence of the - /// fact that it is impossible to construct a BinaryColumn without already - /// having a ref to a valid underlying node structure. This, in turn, is an - /// unfortunate consequence of the fact that a column accessor contains a - /// dynamically allocated root node accessor, and the type of the required - /// root node accessor depends on the size of the B+-tree. - std::unique_ptr m_changesets; - - void update_from_ref(ref_type, version_type); -}; - } // namespace _impl } // namespace realm diff --git a/Pods/Realm/include/core/realm/impl/destroy_guard.hpp b/Pods/Realm/include/core/realm/impl/destroy_guard.hpp index e744141..f5b5e37 100644 --- a/Pods/Realm/include/core/realm/impl/destroy_guard.hpp +++ b/Pods/Realm/include/core/realm/impl/destroy_guard.hpp @@ -39,6 +39,10 @@ class DestroyGuard { ~DestroyGuard() noexcept; + // Default implementations of copy/assign can trigger multiple destructions + DestroyGuard(const DestroyGuard&) = delete; + DestroyGuard& operator=(const DestroyGuard&) = delete; + void reset(T*) noexcept; T* get() const noexcept; @@ -62,6 +66,10 @@ class DeepArrayDestroyGuard { ~DeepArrayDestroyGuard() noexcept; + // Default implementations of copy/assign can trigger multiple destructions + DeepArrayDestroyGuard(const DeepArrayDestroyGuard&) = delete; + DeepArrayDestroyGuard& operator=(const DeepArrayDestroyGuard&) = delete; + void reset(Array*) noexcept; Array* get() const noexcept; @@ -83,6 +91,10 @@ class DeepArrayRefDestroyGuard { ~DeepArrayRefDestroyGuard() noexcept; + // Default implementations of copy/assign can trigger multiple destructions + DeepArrayRefDestroyGuard(const DeepArrayRefDestroyGuard&) = delete; + DeepArrayRefDestroyGuard& operator=(const DeepArrayRefDestroyGuard&) = delete; + void reset(ref_type) noexcept; ref_type get() const noexcept; diff --git a/Pods/Realm/include/core/realm/impl/input_stream.hpp b/Pods/Realm/include/core/realm/impl/input_stream.hpp index b324929..80db30a 100644 --- a/Pods/Realm/include/core/realm/impl/input_stream.hpp +++ b/Pods/Realm/include/core/realm/impl/input_stream.hpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include @@ -63,7 +63,7 @@ class SimpleInputStream : public InputStream { size_t n = std::min(size, size_t(m_end - m_ptr)); const char* begin = m_ptr; m_ptr += n; - std::copy_n(begin, n, buffer); + realm::safe_copy_n(begin, n, buffer); return n; } @@ -159,7 +159,7 @@ class MultiLogNoCopyInputStream : public NoCopyInputStream { // Replication::InputStream such that blocks can be handed over // without copying. This is a straight forward change, but the // result is going to be more complicated and less conventional. - std::copy_n(data, size_2, buffer); + realm::safe_copy_n(data, size_2, buffer); return size_2; } diff --git a/Pods/Realm/include/core/realm/impl/instructions.hpp b/Pods/Realm/include/core/realm/impl/instructions.hpp deleted file mode 100644 index 9f54d81..0000000 --- a/Pods/Realm/include/core/realm/impl/instructions.hpp +++ /dev/null @@ -1,380 +0,0 @@ -/************************************************************************* - * - * REALM CONFIDENTIAL - * __________________ - * - * [2011] - [2015] Realm Inc - * All Rights Reserved. - * - * NOTICE: All information contained herein is, and remains - * the property of Realm Incorporated and its suppliers, - * if any. The intellectual and technical concepts contained - * herein are proprietary to Realm Incorporated - * and its suppliers and may be covered by U.S. and Foreign Patents, - * patents in process, and are protected by trade secret or copyright law. - * Dissemination of this information or reproduction of this material - * is strictly forbidden unless prior written permission is obtained - * from Realm Incorporated. - * - **************************************************************************/ - -#ifndef REALM_IMPL_INSTRUCTIONS_HPP -#define REALM_IMPL_INSTRUCTIONS_HPP - -#include // size_t -#include - -#include -#include -#include -#include -#include - -namespace realm { -namespace _impl { - -class TransactLogParser; -class TransactLogEncoder; -class InputStream; - -#define REALM_FOR_EACH_INSTRUCTION_TYPE(X) \ - X(SelectTable) \ - X(SelectDescriptor) \ - X(SelectLinkList) \ - X(InsertGroupLevelTable) \ - X(EraseGroupLevelTable) \ - X(RenameGroupLevelTable) \ - X(MoveGroupLevelTable) \ - X(InsertEmptyRows) \ - X(Remove) \ - X(MoveLastOver) \ - X(Swap) \ - X(MergeRows) \ - X(Set) \ - X(SetDefault) \ - X(SetUnique) \ - X(AddInteger) \ - X(InsertSubstring) \ - X(EraseSubstring) \ - X(ClearTable) \ - X(OptimizeTable) \ - X(InsertColumn) \ - X(EraseColumn) \ - X(RenameColumn) \ - X(MoveColumn) \ - X(AddSearchIndex) \ - X(RemoveSearchIndex) \ - X(SetLinkType) \ - X(LinkListSet) \ - X(LinkListInsert) \ - X(LinkListMove) \ - X(LinkListSwap) \ - X(LinkListErase) \ - X(LinkListClear) \ - - -enum class InstrType { -#define REALM_DEFINE_INSTRUCTION_TYPE(X) X, -REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DEFINE_INSTRUCTION_TYPE) -#undef REALM_DEFINE_INSTRUCTION_TYPE -}; - -struct StringBufferRange { - size_t offset, size; -}; - - -// Note: All specializations must be "POD", so that they can be -// part of a union. -template struct Instr; - -template <> struct Instr { - size_t group_level_ndx; - size_t num_pairs; - size_t pairs[2]; // FIXME: max 1 level of subtables -}; - -template <> struct Instr { - size_t num_pairs; - size_t pairs[2]; // FIXME: max 1 level of subtables -}; - -template <> struct Instr { - size_t col_ndx; - size_t row_ndx; - size_t link_target_group_level_ndx; -}; - -template <> struct Instr { - Instr() {} - size_t table_ndx; - size_t num_tables; - StringBufferRange name; -}; - -template <> struct Instr { - size_t table_ndx; - size_t num_tables; -}; - -template <> struct Instr { - size_t table_ndx; - StringBufferRange new_name; -}; - -template <> struct Instr { - size_t table_ndx_1; - size_t table_ndx_2; -}; - -template <> struct Instr { - size_t row_ndx; - size_t num_rows_to_insert; - size_t prior_num_rows; -}; - -template <> struct Instr { - size_t row_ndx; - size_t num_rows_to_erase; - size_t prior_num_rows; -}; - -template <> struct Instr { - size_t row_ndx; - size_t num_rows_to_erase; - size_t prior_num_rows; -}; - -template <> struct Instr { - size_t row_ndx_1; - size_t row_ndx_2; -}; - -template <> struct Instr { - size_t row_ndx; - size_t new_row_ndx; -}; - -template <> struct Instr { - size_t col_ndx; - size_t row_ndx; - - struct Payload { - DataType type; - - struct LinkPayload { - size_t target_row; // npos means null - size_t target_group_level_ndx; - bool implicit_nullify; - }; - - union PayloadData { - bool boolean; - int64_t integer; - float fnum; - double dnum; - StringBufferRange str; - Timestamp timestamp; - LinkPayload link; - - PayloadData() {} - PayloadData(const PayloadData&) = default; - PayloadData& operator=(const PayloadData&) = default; - }; - PayloadData data; - - bool is_null() const; - }; - - Payload payload; -}; - -template <> struct Instr { - size_t col_ndx; - size_t row_ndx; - int64_t value; -}; - -template <> struct Instr : Instr { -}; - -template <> struct Instr : Instr { - size_t prior_num_rows; -}; - -template<> struct Instr { - Instr() {} - size_t col_ndx; - size_t row_ndx; - size_t pos; - StringBufferRange value; -}; - -template<> struct Instr { - Instr() {} - size_t col_ndx; - size_t row_ndx; - size_t pos; - size_t size; -}; - -template <> struct Instr { -}; - -template <> struct Instr { -}; - -template <> struct Instr { - size_t link_ndx; - size_t value; - size_t prior_size; -}; - -template <> struct Instr { - size_t link_ndx; - size_t value; - size_t prior_size; -}; - -template <> struct Instr { - size_t link_ndx_1; - size_t link_ndx_2; -}; - -template <> struct Instr { - size_t link_ndx; - bool implicit_nullify; - size_t prior_size; -}; - -template <> struct Instr { - size_t link_ndx_1; - size_t link_ndx_2; -}; - -template <> struct Instr { - size_t num_links; -}; - -template <> struct Instr { - size_t col_ndx; - DataType type; - StringBufferRange name; - size_t link_target_table_ndx; - size_t backlink_col_ndx; - bool nullable; -}; - -template <> struct Instr { - size_t col_ndx; - size_t link_target_table_ndx; - size_t backlink_col_ndx; -}; - -template <> struct Instr { - size_t col_ndx; - StringBufferRange new_name; -}; - -template <> struct Instr { - size_t col_ndx_1; - size_t col_ndx_2; -}; - -template <> struct Instr { - size_t col_ndx; -}; - -template <> struct Instr { - size_t col_ndx; -}; - -template <> struct Instr { - size_t col_ndx; - LinkType type; -}; - -struct AnyInstruction { - using Type = InstrType; - - AnyInstruction() {} - template - AnyInstruction(Instr instr): type(t) - { - get_as() = std::move(instr); - } - - InstrType type; - union InstrUnion { -#define REALM_DEFINE_INSTRUCTION_MEMBER(X) Instr m_ ## X; - REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DEFINE_INSTRUCTION_MEMBER) -#undef REALM_DEFINE_INSTRUCTION_MEMBER - - InstrUnion() { -#if defined(REALM_DEBUG) - char* mem = reinterpret_cast(this); - std::fill(mem, mem + sizeof(*this), 0xef); -#endif // REALM_DEBUG - } - - InstrUnion(const InstrUnion&) = default; - InstrUnion& operator=(const InstrUnion&) = default; - }; - InstrUnion instr; - - template - void visit(F lambda); - template - void visit(F lambda) const; - - template Instr& get_as(); - - template - const Instr& get_as() const - { - return const_cast(this)->template get_as(); - } -}; - -#define REALM_DEFINE_GETTER(X) \ - template<> inline Instr& AnyInstruction::get_as() \ - { \ - return instr.m_ ## X; \ - } - REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DEFINE_GETTER) -#undef REALM_DEFINE_GETTER - -using InstructionList = std::vector; // FIXME: Consider using std::deque - -InstructionList parse_changeset_as_instructions(_impl::TransactLogParser&, _impl::InputStream&, - util::StringBuffer&); -void encode_instructions_as_changeset(const InstructionList&, const util::StringBuffer&, - _impl::TransactLogEncoder&); - - - - -/// Implementation: - -template -void AnyInstruction::visit(F lambda) -{ - switch (type) { -#define REALM_VISIT_INSTRUCTION(X) \ - case InstrType::X: return lambda(get_as()); - REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_VISIT_INSTRUCTION) -#undef REALM_VISIT_INSTRUCTION - } - REALM_UNREACHABLE(); -} - -template -void AnyInstruction::visit(F lambda) const -{ - const_cast(this)->visit(lambda); -} - -} // namespace _impl -} // namespace realm - -#endif // REALM_IMPL_INSTRUCTIONS_HPP diff --git a/Pods/Realm/include/core/realm/impl/sequential_getter.hpp b/Pods/Realm/include/core/realm/impl/sequential_getter.hpp index 0cb445f..c7dd866 100644 --- a/Pods/Realm/include/core/realm/impl/sequential_getter.hpp +++ b/Pods/Realm/include/core/realm/impl/sequential_getter.hpp @@ -40,9 +40,7 @@ class SequentialGetter : public SequentialGetterBase { SequentialGetter(const Table& table, size_t column_ndx) { - if (column_ndx != not_found) - m_column = static_cast(&table.get_column_base(column_ndx)); - init(m_column); + init(static_cast(&table.get_column_base(column_ndx))); } SequentialGetter(const ColType* column) @@ -56,6 +54,7 @@ class SequentialGetter : public SequentialGetterBase { void init(const ColType* column) { + REALM_ASSERT(column != nullptr); m_array_ptr.reset(); // Explicitly destroy the old one first, because we're reusing the memory. m_array_ptr.reset(new (&m_leaf_accessor_storage) ArrayType(column->get_alloc())); m_column = column; @@ -93,9 +92,10 @@ class SequentialGetter : public SequentialGetterBase { // FIXME: Below optimization is skipped because array leafs might relocate during the lifetime of the // object that owns this SequentialGetter. Enable again when we have proper support for that. - cache_next(index); - T av = m_leaf_ptr->get(index - m_leaf_start); - return av; +// +// cache_next(index); +// T av = m_leaf_ptr->get(index - m_leaf_start); +// return av; #ifdef _MSC_VER #pragma warning(pop) @@ -110,8 +110,8 @@ class SequentialGetter : public SequentialGetterBase { return global_end - m_leaf_start; } - size_t m_leaf_start; - size_t m_leaf_end; + size_t m_leaf_start = 0; + size_t m_leaf_end = 0; const ColType* m_column = nullptr; const ArrayType* m_leaf_ptr = nullptr; diff --git a/Pods/Realm/include/core/realm/impl/table_path.hpp b/Pods/Realm/include/core/realm/impl/table_path.hpp deleted file mode 100644 index c03b6cb..0000000 --- a/Pods/Realm/include/core/realm/impl/table_path.hpp +++ /dev/null @@ -1,88 +0,0 @@ -/************************************************************************* - * - * REALM CONFIDENTIAL - * __________________ - * - * [2011] - [2012] Realm Inc - * All Rights Reserved. - * - * NOTICE: All information contained herein is, and remains - * the property of Realm Incorporated and its suppliers, - * if any. The intellectual and technical concepts contained - * herein are proprietary to Realm Incorporated - * and its suppliers and may be covered by U.S. and Foreign Patents, - * patents in process, and are protected by trade secret or copyright law. - * Dissemination of this information or reproduction of this material - * is strictly forbidden unless prior written permission is obtained - * from Realm Incorporated. - * - **************************************************************************/ - -#ifndef REALM_IMPL_TABLE_PATH_HPP -#define REALM_IMPL_TABLE_PATH_HPP - -#include -#include -#include - -namespace realm { -namespace _impl { - -class TablePath { -public: - TablePath() - { - } - - TablePath(const size_t* begin, size_t len) : m_coords(begin, begin + len) - { - } - - TablePath(size_t group_level_ndx, size_t num_pairs, const size_t* pairs) - { - m_coords.reserve(num_pairs * 2 + 1); - m_coords.push_back(group_level_ndx); - std::copy(pairs, pairs + num_pairs * 2, std::back_inserter(m_coords)); - } - - bool operator==(const TablePath& other) const - { - if (m_coords.size() != other.m_coords.size()) { - return false; - } - for (size_t i = 0; i < m_coords.size(); ++i) { - if (m_coords[i] != other.m_coords[i]) { - return false; - } - } - return true; - } - - bool operator!=(const TablePath& other) const - { - return !((*this) == other); - } - - void push(size_t coord) - { - m_coords.push_back(coord); - } - - size_t size() const - { - return m_coords.size(); - } - - void clear() - { - m_coords.clear(); - } - - // FIXME: Should be private, but is accessed directly from sync.cpp. - std::vector m_coords; -}; - -} // namespace _impl -} // namespace realm - -#endif // REALM_IMPL_TABLE_PATH_HPP diff --git a/Pods/Realm/include/core/realm/impl/transact_log.hpp b/Pods/Realm/include/core/realm/impl/transact_log.hpp index 8941c38..ee3c710 100644 --- a/Pods/Realm/include/core/realm/impl/transact_log.hpp +++ b/Pods/Realm/include/core/realm/impl/transact_log.hpp @@ -26,10 +26,8 @@ #include #include #include -#include #include #include -#include #include #include @@ -45,7 +43,7 @@ enum Instruction { instr_InsertGroupLevelTable = 1, instr_EraseGroupLevelTable = 2, // Remove columnless table from group instr_RenameGroupLevelTable = 3, - instr_MoveGroupLevelTable = 4, + instr_MoveGroupLevelTable = 4, // UNSUPPORTED/UNUSED. FIXME: remove in next breaking change instr_SelectTable = 5, instr_Set = 6, instr_SetUnique = 7, @@ -57,33 +55,34 @@ enum Instruction { instr_InsertEmptyRows = 13, instr_EraseRows = 14, // Remove (multiple) rows instr_SwapRows = 15, - instr_MergeRows = 16, // Replace links pointing to row A with links to row B - instr_ClearTable = 17, // Remove all rows in selected table - instr_OptimizeTable = 18, - instr_SelectDescriptor = 19, // Select descriptor from currently selected root table + instr_MoveRow = 16, + instr_MergeRows = 17, // Replace links pointing to row A with links to row B + instr_ClearTable = 18, // Remove all rows in selected table + instr_OptimizeTable = 19, + instr_SelectDescriptor = 20, // Select descriptor from currently selected root table instr_InsertColumn = - 20, // Insert new non-nullable column into to selected descriptor (nullable is instr_InsertNullableColumn) - instr_InsertLinkColumn = 21, // do, but for a link-type column - instr_InsertNullableColumn = 22, // Insert nullable column - instr_EraseColumn = 23, // Remove column from selected descriptor - instr_EraseLinkColumn = 24, // Remove link-type column from selected descriptor - instr_RenameColumn = 25, // Rename column in selected descriptor - instr_MoveColumn = 26, // Move column in selected descriptor - instr_AddSearchIndex = 27, // Add a search index to a column - instr_RemoveSearchIndex = 28, // Remove a search index from a column - instr_SetLinkType = 29, // Strong/weak - instr_SelectLinkList = 30, - instr_LinkListSet = 31, // Assign to link list entry - instr_LinkListInsert = 32, // Insert entry into link list - instr_LinkListMove = 33, // Move an entry within a link list - instr_LinkListSwap = 34, // Swap two entries within a link list - instr_LinkListErase = 35, // Remove an entry from a link list - instr_LinkListNullify = 36, // Remove an entry from a link list due to linked row being erased - instr_LinkListClear = 37, // Ramove all entries from a link list - instr_LinkListSetAll = 38, // Assign to link list entry + 21, // Insert new non-nullable column into to selected descriptor (nullable is instr_InsertNullableColumn) + instr_InsertLinkColumn = 22, // do, but for a link-type column + instr_InsertNullableColumn = 23, // Insert nullable column + instr_EraseColumn = 24, // Remove column from selected descriptor + instr_EraseLinkColumn = 25, // Remove link-type column from selected descriptor + instr_RenameColumn = 26, // Rename column in selected descriptor + instr_MoveColumn = 27, // Move column in selected descriptor (UNSUPPORTED/UNUSED) FIXME: remove + instr_AddSearchIndex = 28, // Add a search index to a column + instr_RemoveSearchIndex = 29, // Remove a search index from a column + instr_SetLinkType = 30, // Strong/weak + instr_SelectLinkList = 31, + instr_LinkListSet = 32, // Assign to link list entry + instr_LinkListInsert = 33, // Insert entry into link list + instr_LinkListMove = 34, // Move an entry within a link list + instr_LinkListSwap = 35, // Swap two entries within a link list + instr_LinkListErase = 36, // Remove an entry from a link list + instr_LinkListNullify = 37, // Remove an entry from a link list due to linked row being erased + instr_LinkListClear = 38, // Ramove all entries from a link list + instr_LinkListSetAll = 39, // Assign to link list entry + instr_AddRowWithKey = 40, // Insert a row with a given key }; - class TransactLogStream { public: /// Ensure contiguous free space in the transaction log @@ -149,16 +148,16 @@ class NullInstructionObserver { { return true; } - bool move_group_level_table(size_t, size_t) - { - return true; - } // Must have table selected: bool insert_empty_rows(size_t, size_t, size_t, bool) { return true; } + bool add_row_with_key(size_t, size_t, size_t, int64_t) + { + return true; + } bool erase_rows(size_t, size_t, size_t, bool) { return true; @@ -167,11 +166,15 @@ class NullInstructionObserver { { return true; } + bool move_row(size_t, size_t) + { + return true; + } bool merge_rows(size_t, size_t) { return true; } - bool clear_table() + bool clear_table(size_t) { return true; } @@ -265,10 +268,6 @@ class NullInstructionObserver { { return true; } - bool move_column(size_t, size_t) - { - return true; - } bool add_search_index(size_t) { return true; @@ -333,14 +332,15 @@ class TransactLogEncoder { bool insert_group_level_table(size_t table_ndx, size_t num_tables, StringData name); bool erase_group_level_table(size_t table_ndx, size_t num_tables); bool rename_group_level_table(size_t table_ndx, StringData new_name); - bool move_group_level_table(size_t from_table_ndx, size_t to_table_ndx); /// Must have table selected. bool insert_empty_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, bool unordered); + bool add_row_with_key(size_t row_ndx, size_t prior_num_rows, size_t key_col_ndx, int64_t key); bool erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, bool unordered); bool swap_rows(size_t row_ndx_1, size_t row_ndx_2); + bool move_row(size_t from_ndx, size_t to_ndx); bool merge_rows(size_t row_ndx, size_t new_row_ndx); - bool clear_table(); + bool clear_table(size_t old_table_size); bool set_int(size_t col_ndx, size_t row_ndx, int_fast64_t, Instruction = instr_Set, size_t = 0); bool add_int(size_t col_ndx, size_t row_ndx, int_fast64_t); @@ -367,7 +367,6 @@ class TransactLogEncoder { bool erase_link_column(size_t col_ndx, size_t link_target_table_ndx, size_t backlink_col_ndx); bool erase_column(size_t col_ndx); bool rename_column(size_t col_ndx, StringData new_name); - bool move_column(size_t col_ndx_1, size_t col_ndx_2); bool add_search_index(size_t col_ndx); bool remove_search_index(size_t col_ndx); bool set_link_type(size_t col_ndx, LinkType); @@ -393,12 +392,21 @@ class TransactLogEncoder { } private: + using IntegerList = std::tuple; + using UnsignedList = std::tuple; + // Make sure this is in agreement with the actual integer encoding // scheme (see encode_int()). - static const int max_enc_bytes_per_int = 10; - static const int max_enc_bytes_per_double = sizeof(double); - static const int max_enc_bytes_per_num = + static constexpr int max_enc_bytes_per_int = 10; + static constexpr int max_enc_bytes_per_double = sizeof(double); + static constexpr int max_enc_bytes_per_num = max_enc_bytes_per_int < max_enc_bytes_per_double ? max_enc_bytes_per_double : max_enc_bytes_per_int; +// Space is reserved in chunks to avoid excessive over allocation. +#ifdef REALM_DEBUG + static constexpr int max_numbers_per_chunk = 2; // Increase the chance of chunking in debug mode +#else + static constexpr int max_numbers_per_chunk = 8; +#endif // This value is used in Set* instructions in place of the 'type' field in // the stream to indicate that the value of the Set* instruction is NULL, @@ -420,81 +428,100 @@ class TransactLogEncoder { /// \param ptr Must be in the range [m_transact_log_free_begin, m_transact_log_free_end] void advance(char* ptr) noexcept; - template - void append_simple_instr(Instruction, const util::Tuple& numbers); + template + size_t max_size(T); + + size_t max_size_list() + { + return 0; + } - template - void append_string_instr(Instruction, const util::Tuple& numbers, StringData); + template + size_t max_size_list(T val, Args... args) + { + return max_size(val) + max_size_list(args...); + } - template - void append_mixed_instr(Instruction, const util::Tuple& numbers, const Mixed&); + template + char* encode(char* ptr, T value); - template - bool append_variable_size_instr(Instruction instr, const util::Tuple& numbers, I var_begin, I var_end); + char* encode_list(char* ptr) + { + advance(ptr); + return ptr; + } + + template + char* encode_list(char* ptr, T value, Args... args) + { + return encode_list(encode(ptr, value), args...); + } + + template + void append_simple_instr(L... numbers); + + template + void append_mixed_instr(Instruction instr, const Mixed& value, L... numbers); template static char* encode_int(char*, T value); - static char* encode_bool(char*, bool value); - static char* encode_float(char*, float value); - static char* encode_double(char*, double value); - template - struct EncodeNumber; friend class TransactLogParser; }; class TransactLogConvenientEncoder { public: - void insert_group_level_table(size_t table_ndx, size_t num_tables, StringData name); - void erase_group_level_table(size_t table_ndx, size_t num_tables); - void rename_group_level_table(size_t table_ndx, StringData new_name); - void move_group_level_table(size_t from_table_ndx, size_t to_table_ndx); - void insert_column(const Descriptor&, size_t col_ndx, DataType type, StringData name, LinkTargetInfo& link, - bool nullable = false); - void erase_column(const Descriptor&, size_t col_ndx); - void rename_column(const Descriptor&, size_t col_ndx, StringData name); - void move_column(const Descriptor&, size_t from, size_t to); - - void set_int(const Table*, size_t col_ndx, size_t ndx, int_fast64_t value, Instruction variant = instr_Set); - void add_int(const Table*, size_t col_ndx, size_t ndx, int_fast64_t value); - void set_bool(const Table*, size_t col_ndx, size_t ndx, bool value, Instruction variant = instr_Set); - void set_float(const Table*, size_t col_ndx, size_t ndx, float value, Instruction variant = instr_Set); - void set_double(const Table*, size_t col_ndx, size_t ndx, double value, Instruction variant = instr_Set); - void set_string(const Table*, size_t col_ndx, size_t ndx, StringData value, Instruction variant = instr_Set); - void set_binary(const Table*, size_t col_ndx, size_t ndx, BinaryData value, Instruction variant = instr_Set); - void set_olddatetime(const Table*, size_t col_ndx, size_t ndx, OldDateTime value, - Instruction variant = instr_Set); - void set_timestamp(const Table*, size_t col_ndx, size_t ndx, Timestamp value, Instruction variant = instr_Set); - void set_table(const Table*, size_t col_ndx, size_t ndx, Instruction variant = instr_Set); - void set_mixed(const Table*, size_t col_ndx, size_t ndx, const Mixed& value, Instruction variant = instr_Set); - void set_link(const Table*, size_t col_ndx, size_t ndx, size_t value, Instruction variant = instr_Set); - void set_null(const Table*, size_t col_ndx, size_t ndx, Instruction variant = instr_Set); - void set_link_list(const LinkView&, const IntegerColumn& values); - void insert_substring(const Table*, size_t col_ndx, size_t row_ndx, size_t pos, StringData); - void erase_substring(const Table*, size_t col_ndx, size_t row_ndx, size_t pos, size_t size); + virtual void insert_group_level_table(size_t table_ndx, size_t num_tables, StringData name); + virtual void erase_group_level_table(size_t table_ndx, size_t num_tables); + virtual void rename_group_level_table(size_t table_ndx, StringData new_name); + virtual void insert_column(const Descriptor&, size_t col_ndx, DataType type, StringData name, LinkTargetInfo& link, + bool nullable = false); + virtual void erase_column(const Descriptor&, size_t col_ndx); + virtual void rename_column(const Descriptor&, size_t col_ndx, StringData name); + + virtual void set_int(const Table*, size_t col_ndx, size_t ndx, int_fast64_t value, Instruction variant = instr_Set); + virtual void add_int(const Table*, size_t col_ndx, size_t ndx, int_fast64_t value); + virtual void set_bool(const Table*, size_t col_ndx, size_t ndx, bool value, Instruction variant = instr_Set); + virtual void set_float(const Table*, size_t col_ndx, size_t ndx, float value, Instruction variant = instr_Set); + virtual void set_double(const Table*, size_t col_ndx, size_t ndx, double value, Instruction variant = instr_Set); + virtual void set_string(const Table*, size_t col_ndx, size_t ndx, StringData value, Instruction variant = instr_Set); + virtual void set_binary(const Table*, size_t col_ndx, size_t ndx, BinaryData value, Instruction variant = instr_Set); + virtual void set_olddatetime(const Table*, size_t col_ndx, size_t ndx, OldDateTime value, + Instruction variant = instr_Set); + virtual void set_timestamp(const Table*, size_t col_ndx, size_t ndx, Timestamp value, Instruction variant = instr_Set); + virtual void set_table(const Table*, size_t col_ndx, size_t ndx, Instruction variant = instr_Set); + virtual void set_mixed(const Table*, size_t col_ndx, size_t ndx, const Mixed& value, Instruction variant = instr_Set); + virtual void set_link(const Table*, size_t col_ndx, size_t ndx, size_t value, Instruction variant = instr_Set); + virtual void set_null(const Table*, size_t col_ndx, size_t ndx, Instruction variant = instr_Set); + virtual void set_link_list(const LinkView&, const IntegerColumn& values); + virtual void insert_substring(const Table*, size_t col_ndx, size_t row_ndx, size_t pos, StringData); + virtual void erase_substring(const Table*, size_t col_ndx, size_t row_ndx, size_t pos, size_t size); /// \param prior_num_rows The number of rows in the table prior to the /// modification. - void insert_empty_rows(const Table*, size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows); + virtual void insert_empty_rows(const Table*, size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows); + virtual void add_row_with_key(const Table* t, size_t row_ndx, size_t prior_num_rows, size_t key_col_ndx, + int64_t key); /// \param prior_num_rows The number of rows in the table prior to the /// modification. - void erase_rows(const Table*, size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, - bool is_move_last_over); - - void swap_rows(const Table*, size_t row_ndx_1, size_t row_ndx_2); - void merge_rows(const Table*, size_t row_ndx, size_t new_row_ndx); - void add_search_index(const Table*, size_t col_ndx); - void remove_search_index(const Table*, size_t col_ndx); - void set_link_type(const Table*, size_t col_ndx, LinkType); - void clear_table(const Table*); - void optimize_table(const Table*); - - void link_list_set(const LinkView&, size_t link_ndx, size_t value); - void link_list_insert(const LinkView&, size_t link_ndx, size_t value); - void link_list_move(const LinkView&, size_t from_link_ndx, size_t to_link_ndx); - void link_list_swap(const LinkView&, size_t link_ndx_1, size_t link_ndx_2); - void link_list_erase(const LinkView&, size_t link_ndx); - void link_list_clear(const LinkView&); + virtual void erase_rows(const Table*, size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, + bool is_move_last_over); + + virtual void swap_rows(const Table*, size_t row_ndx_1, size_t row_ndx_2); + virtual void move_row(const Table*, size_t from_ndx, size_t to_ndx); + virtual void merge_rows(const Table*, size_t row_ndx, size_t new_row_ndx); + virtual void add_search_index(const Descriptor&, size_t col_ndx); + virtual void remove_search_index(const Descriptor&, size_t col_ndx); + virtual void set_link_type(const Table*, size_t col_ndx, LinkType); + virtual void clear_table(const Table*, size_t prior_num_rows); + virtual void optimize_table(const Table*); + + virtual void link_list_set(const LinkView&, size_t link_ndx, size_t value); + virtual void link_list_insert(const LinkView&, size_t link_ndx, size_t value); + virtual void link_list_move(const LinkView&, size_t from_link_ndx, size_t to_link_ndx); + virtual void link_list_swap(const LinkView&, size_t link_ndx_1, size_t link_ndx_2); + virtual void link_list_erase(const LinkView&, size_t link_ndx); + virtual void link_list_clear(const LinkView&); //@{ @@ -505,8 +532,8 @@ class TransactLogConvenientEncoder { /// (reactor pattern) to be explicitly notified about the implicit /// nullifications. - void nullify_link(const Table*, size_t col_ndx, size_t ndx); - void link_list_nullify(const LinkView&, size_t link_ndx); + virtual void nullify_link(const Table*, size_t col_ndx, size_t ndx); + virtual void link_list_nullify(const LinkView&, size_t link_ndx); //@} @@ -647,7 +674,7 @@ inline void TransactLogBufferStream::transact_log_append(const char* data, size_ char** out_new_end) { transact_log_reserve(size, out_new_begin, out_new_end); - *out_new_begin = std::copy_n(data, size, *out_new_begin); + *out_new_begin = realm::safe_copy_n(data, size, *out_new_begin); } inline const char* TransactLogBufferStream::transact_log_data() const @@ -767,184 +794,211 @@ char* TransactLogEncoder::encode_int(char* ptr, T value) return ++ptr; } -inline char* TransactLogEncoder::encode_bool(char* ptr, bool value) +template +char* TransactLogEncoder::encode(char* ptr, T value) +{ + auto value_2 = value + 0; // Perform integral promotion + return encode_int(ptr, value_2); +} + +template <> +inline char* TransactLogEncoder::encode(char* ptr, char value) { - // A `char` is the smallest element that the encoder/decoder can process. So we encode the bool - // in a char. If we called encode_int it would end up as a char too, but we would get - // Various warnings about arithmetic on non-arithmetic type. - return encode_int(ptr, value); + // Write the char as-is without encoding. + *ptr++ = value; + return ptr; } -inline char* TransactLogEncoder::encode_float(char* ptr, float value) +template <> +inline char* TransactLogEncoder::encode(char* ptr, Instruction inst) +{ + return encode(ptr, inst); +} + +template <> +inline char* TransactLogEncoder::encode(char* ptr, bool value) +{ + return encode(ptr, value); +} + +template <> +inline char* TransactLogEncoder::encode(char* ptr, float value) { static_assert(std::numeric_limits::is_iec559 && sizeof(float) * std::numeric_limits::digits == 32, "Unsupported 'float' representation"); const char* val_ptr = reinterpret_cast(&value); - return std::copy_n(val_ptr, sizeof value, ptr); + return realm::safe_copy_n(val_ptr, sizeof value, ptr); } -inline char* TransactLogEncoder::encode_double(char* ptr, double value) +template <> +inline char* TransactLogEncoder::encode(char* ptr, double value) { static_assert(std::numeric_limits::is_iec559 && sizeof(double) * std::numeric_limits::digits == 64, "Unsupported 'double' representation"); const char* val_ptr = reinterpret_cast(&value); - return std::copy_n(val_ptr, sizeof value, ptr); + return realm::safe_copy_n(val_ptr, sizeof value, ptr); } -template -struct TransactLogEncoder::EncodeNumber { - void operator()(T value, char** ptr) - { - auto value_2 = value + 0; // Perform integral promotion - *ptr = encode_int(*ptr, value_2); - } -}; template <> -struct TransactLogEncoder::EncodeNumber { - void operator()(char value, char** ptr) - { - // Write the char as-is without encoding. - **ptr = value; - ++(*ptr); - } -}; +inline char* TransactLogEncoder::encode(char* ptr, DataType type) +{ + return encode(ptr, type); +} + template <> -struct TransactLogEncoder::EncodeNumber { - void operator()(bool value, char** ptr) - { - *ptr = encode_bool(*ptr, value); - } -}; +inline char* TransactLogEncoder::encode(char* ptr, StringData s) +{ + ptr = encode_int(ptr, s.size()); + return std::copy_n(s.data(), s.size(), ptr); +} + template <> -struct TransactLogEncoder::EncodeNumber { - void operator()(float value, char** ptr) - { - *ptr = encode_float(*ptr, value); +inline char* TransactLogEncoder::encode(char* ptr, + TransactLogEncoder::IntegerList list) +{ + IntegerColumnIterator i = std::get<0>(list); + IntegerColumnIterator end = std::get<1>(list); + + while (end - i > max_numbers_per_chunk) { + for (int j = 0; j < max_numbers_per_chunk; ++j) + ptr = encode_int(ptr, *i++); + advance(ptr); + size_t max_required_bytes_2 = max_enc_bytes_per_num * max_numbers_per_chunk; + ptr = reserve(max_required_bytes_2); // Throws } -}; + + while (i != end) + ptr = encode_int(ptr, *i++); + + return ptr; +} + template <> -struct TransactLogEncoder::EncodeNumber { - void operator()(double value, char** ptr) - { - *ptr = encode_double(*ptr, value); - } -}; +inline char* TransactLogEncoder::encode(char* ptr, + TransactLogEncoder::UnsignedList list) +{ + const size_t* i = std::get<0>(list); + const size_t* end = std::get<1>(list); + + while (i != end) + ptr = encode_int(ptr, *i++); + + return ptr; +} + +template +size_t TransactLogEncoder::max_size(T) +{ + return max_enc_bytes_per_num; +} + template <> -struct TransactLogEncoder::EncodeNumber { - void operator()(DataType type, char** ptr) - { - auto value_2 = type + 0; // Perform integral promotion - *ptr = encode_int(*ptr, value_2); - } -}; +inline size_t TransactLogEncoder::max_size(char) +{ + return 1; +} -template -void TransactLogEncoder::append_simple_instr(Instruction instr, const util::Tuple& numbers) +template <> +inline size_t TransactLogEncoder::max_size(bool) { - size_t num_numbers = util::TypeCount::value; - size_t max_required_bytes = 1 + max_enc_bytes_per_num * num_numbers; - char* ptr = reserve(max_required_bytes); // Throws - *ptr++ = char(instr); - util::for_each(numbers, &ptr); - advance(ptr); + return 1; } -template -void TransactLogEncoder::append_string_instr(Instruction instr, const util::Tuple& numbers, StringData string) +template <> +inline size_t TransactLogEncoder::max_size(Instruction) { - size_t num_numbers = util::TypeCount::value + 1; - size_t max_required_bytes = 1 + max_enc_bytes_per_num * num_numbers + string.size(); + return 1; +} + +template <> +inline size_t TransactLogEncoder::max_size(DataType) +{ + return 1; +} + +template <> +inline size_t TransactLogEncoder::max_size(StringData s) +{ + return max_enc_bytes_per_num + s.size(); +} + +template <> +inline size_t TransactLogEncoder::max_size(IntegerList) +{ + // We only allocate space for 'max_numbers_per_chunk' at a time + return max_enc_bytes_per_num * max_numbers_per_chunk; +} + +template <> +inline size_t TransactLogEncoder::max_size(UnsignedList list) +{ + const size_t* begin = std::get<0>(list); + const size_t* end = std::get<1>(list); + // list contains (end - begin) elements + return max_enc_bytes_per_num * (end - begin); +} + +template +void TransactLogEncoder::append_simple_instr(L... numbers) +{ + size_t max_required_bytes = max_size_list(numbers...); char* ptr = reserve(max_required_bytes); // Throws - *ptr++ = char(instr); - util::for_each(append(numbers, string.size()), &ptr); - ptr = std::copy_n(string.data(), string.size(), ptr); - advance(ptr); + encode_list(ptr, numbers...); } -template -void TransactLogEncoder::append_mixed_instr(Instruction instr, const util::Tuple& numbers, const Mixed& value) +template +void TransactLogEncoder::append_mixed_instr(Instruction instr, const Mixed& value, L... numbers) { DataType type = value.get_type(); - auto numbers_2 = append(numbers, type); switch (type) { case type_Int: - append_simple_instr(instr, append(numbers_2, value.get_int())); // Throws + append_simple_instr(instr, numbers..., type, value.get_int()); // Throws return; case type_Bool: - append_simple_instr(instr, append(numbers_2, value.get_bool())); // Throws + append_simple_instr(instr, numbers..., type, value.get_bool()); // Throws return; case type_Float: - append_simple_instr(instr, append(numbers_2, value.get_float())); // Throws + append_simple_instr(instr, numbers..., type, value.get_float()); // Throws return; case type_Double: - append_simple_instr(instr, append(numbers_2, value.get_double())); // Throws + append_simple_instr(instr, numbers..., type, value.get_double()); // Throws return; case type_OldDateTime: { auto value_2 = value.get_olddatetime().get_olddatetime(); - append_simple_instr(instr, append(numbers_2, value_2)); // Throws + append_simple_instr(instr, numbers..., type, value_2); // Throws return; } case type_String: { - append_string_instr(instr, numbers_2, value.get_string()); // Throws + append_simple_instr(instr, numbers..., type, value.get_string()); // Throws return; } case type_Binary: { BinaryData value_2 = value.get_binary(); StringData value_3(value_2.data(), value_2.size()); - append_string_instr(instr, numbers_2, value_3); // Throws + append_simple_instr(instr, numbers..., type, value_3); // Throws return; } case type_Timestamp: { Timestamp ts = value.get_timestamp(); int64_t seconds = ts.get_seconds(); int32_t nano_seconds = ts.get_nanoseconds(); - auto numbers_3 = append(numbers_2, seconds); - append_simple_instr(instr, append(numbers_3, nano_seconds)); // Throws + append_simple_instr(instr, numbers..., type, seconds, nano_seconds); // Throws return; } case type_Table: - append_simple_instr(instr, numbers_2); // Throws + append_simple_instr(instr, numbers..., type); // Throws return; case type_Mixed: // Mixed in mixed is not possible - REALM_ASSERT_RELEASE(false); + REALM_TERMINATE("Mixed in Mixed not possible"); case type_Link: case type_LinkList: // FIXME: Need to handle new link types here. - REALM_ASSERT_RELEASE(false); - } - REALM_ASSERT_RELEASE(false); -} - -template -bool TransactLogEncoder::append_variable_size_instr(Instruction instr, const util::Tuple& numbers, I var_begin, - I var_end) -{ -// Space is reserved in chunks to avoid excessive over allocation. -#ifdef REALM_DEBUG - const int max_numbers_per_chunk = 2; // Increase the chance of chunking in debug mode -#else - const int max_numbers_per_chunk = 8; -#endif - size_t num_numbers = util::TypeCount::value + max_numbers_per_chunk; - size_t max_required_bytes = 1 + max_enc_bytes_per_num * num_numbers; - char* ptr = reserve(max_required_bytes); // Throws - *ptr++ = char(instr); - util::for_each(numbers, &ptr); - I i = var_begin; - while (var_end - i > max_numbers_per_chunk) { - for (int j = 0; j < max_numbers_per_chunk; ++j) - ptr = encode_int(ptr, *i++); - advance(ptr); - size_t max_required_bytes_2 = max_enc_bytes_per_num * max_numbers_per_chunk; - ptr = reserve(max_required_bytes_2); // Throws + REALM_TERMINATE("Link types in Mixed not supported."); } - while (i != var_end) - ptr = encode_int(ptr, *i++); - advance(ptr); - return true; + REALM_TERMINATE("Invalid Mixed."); } inline void TransactLogConvenientEncoder::unselect_all() noexcept @@ -991,7 +1045,7 @@ inline void TransactLogConvenientEncoder::select_link_list(const LinkView& list) inline bool TransactLogEncoder::insert_group_level_table(size_t table_ndx, size_t prior_num_tables, StringData name) { - append_string_instr(instr_InsertGroupLevelTable, util::tuple(table_ndx, prior_num_tables), name); // Throws + append_simple_instr(instr_InsertGroupLevelTable, table_ndx, prior_num_tables, name); // Throws return true; } @@ -1004,7 +1058,7 @@ inline void TransactLogConvenientEncoder::insert_group_level_table(size_t table_ inline bool TransactLogEncoder::erase_group_level_table(size_t table_ndx, size_t prior_num_tables) { - append_simple_instr(instr_EraseGroupLevelTable, util::tuple(table_ndx, prior_num_tables)); // Throws + append_simple_instr(instr_EraseGroupLevelTable, table_ndx, prior_num_tables); // Throws return true; } @@ -1016,7 +1070,7 @@ inline void TransactLogConvenientEncoder::erase_group_level_table(size_t table_n inline bool TransactLogEncoder::rename_group_level_table(size_t table_ndx, StringData new_name) { - append_string_instr(instr_RenameGroupLevelTable, util::tuple(table_ndx), new_name); // Throws + append_simple_instr(instr_RenameGroupLevelTable, table_ndx, new_name); // Throws return true; } @@ -1026,23 +1080,10 @@ inline void TransactLogConvenientEncoder::rename_group_level_table(size_t table_ m_encoder.rename_group_level_table(table_ndx, new_name); // Throws } -inline bool TransactLogEncoder::move_group_level_table(size_t from_table_ndx, size_t to_table_ndx) -{ - REALM_ASSERT(from_table_ndx != to_table_ndx); - append_simple_instr(instr_MoveGroupLevelTable, util::tuple(from_table_ndx, to_table_ndx)); - return true; -} - -inline void TransactLogConvenientEncoder::move_group_level_table(size_t from_table_ndx, size_t to_table_ndx) -{ - unselect_all(); - m_encoder.move_group_level_table(from_table_ndx, to_table_ndx); -} - inline bool TransactLogEncoder::insert_column(size_t col_ndx, DataType type, StringData name, bool nullable) { Instruction instr = (nullable ? instr_InsertNullableColumn : instr_InsertColumn); - append_string_instr(instr, util::tuple(col_ndx, type), name); // Throws + append_simple_instr(instr, col_ndx, type, name); // Throws return true; } @@ -1050,7 +1091,7 @@ inline bool TransactLogEncoder::insert_link_column(size_t col_ndx, DataType type size_t link_target_table_ndx, size_t backlink_col_ndx) { REALM_ASSERT(_impl::TableFriend::is_link_type(ColumnType(type))); - append_string_instr(instr_InsertLinkColumn, util::tuple(col_ndx, type, link_target_table_ndx, backlink_col_ndx), + append_simple_instr(instr_InsertLinkColumn, col_ndx, type, link_target_table_ndx, backlink_col_ndx, name); // Throws return true; } @@ -1079,15 +1120,14 @@ inline void TransactLogConvenientEncoder::insert_column(const Descriptor& desc, inline bool TransactLogEncoder::erase_column(size_t col_ndx) { - append_simple_instr(instr_EraseColumn, util::tuple(col_ndx)); // Throws + append_simple_instr(instr_EraseColumn, col_ndx); // Throws return true; } inline bool TransactLogEncoder::erase_link_column(size_t col_ndx, size_t link_target_table_ndx, size_t backlink_col_ndx) { - append_simple_instr(instr_EraseLinkColumn, util::tuple(col_ndx, link_target_table_ndx, - backlink_col_ndx)); // Throws + append_simple_instr(instr_EraseLinkColumn, col_ndx, link_target_table_ndx, backlink_col_ndx); // Throws return true; } @@ -1116,7 +1156,7 @@ inline void TransactLogConvenientEncoder::erase_column(const Descriptor& desc, s inline bool TransactLogEncoder::rename_column(size_t col_ndx, StringData new_name) { - append_string_instr(instr_RenameColumn, util::tuple(col_ndx), new_name); // Throws + append_simple_instr(instr_RenameColumn, col_ndx, new_name); // Throws return true; } @@ -1127,27 +1167,14 @@ inline void TransactLogConvenientEncoder::rename_column(const Descriptor& desc, } -inline bool TransactLogEncoder::move_column(size_t from, size_t to) -{ - append_simple_instr(instr_MoveColumn, util::tuple(from, to)); // Throws - return true; -} - -inline void TransactLogConvenientEncoder::move_column(const Descriptor& desc, size_t from, size_t to) -{ - select_desc(desc); // Throws - m_encoder.move_column(from, to); -} - - inline bool TransactLogEncoder::set_int(size_t col_ndx, size_t ndx, int_fast64_t value, Instruction variant, size_t prior_num_rows) { REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault || variant == instr_SetUnique, variant); if (REALM_UNLIKELY(variant == instr_SetUnique)) - append_simple_instr(variant, util::tuple(type_Int, col_ndx, ndx, prior_num_rows, value)); // Throws + append_simple_instr(variant, type_Int, col_ndx, ndx, prior_num_rows, value); // Throws else - append_simple_instr(variant, util::tuple(type_Int, col_ndx, ndx, value)); // Throws + append_simple_instr(variant, type_Int, col_ndx, ndx, value); // Throws return true; } @@ -1162,7 +1189,7 @@ inline void TransactLogConvenientEncoder::set_int(const Table* t, size_t col_ndx inline bool TransactLogEncoder::add_int(size_t col_ndx, size_t ndx, int_fast64_t value) { - append_simple_instr(instr_AddInteger, util::tuple(col_ndx, ndx, value)); // Throws + append_simple_instr(instr_AddInteger, col_ndx, ndx, value); // Throws return true; } @@ -1175,7 +1202,7 @@ inline void TransactLogConvenientEncoder::add_int(const Table* t, size_t col_ndx inline bool TransactLogEncoder::set_bool(size_t col_ndx, size_t ndx, bool value, Instruction variant) { REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant); - append_simple_instr(variant, util::tuple(type_Bool, col_ndx, ndx, value)); // Throws + append_simple_instr(variant, type_Bool, col_ndx, ndx, value); // Throws return true; } @@ -1189,7 +1216,7 @@ inline void TransactLogConvenientEncoder::set_bool(const Table* t, size_t col_nd inline bool TransactLogEncoder::set_float(size_t col_ndx, size_t ndx, float value, Instruction variant) { REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant); - append_simple_instr(variant, util::tuple(type_Float, col_ndx, ndx, value)); // Throws + append_simple_instr(variant, type_Float, col_ndx, ndx, value); // Throws return true; } @@ -1203,7 +1230,7 @@ inline void TransactLogConvenientEncoder::set_float(const Table* t, size_t col_n inline bool TransactLogEncoder::set_double(size_t col_ndx, size_t ndx, double value, Instruction variant) { REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant); - append_simple_instr(instr_Set, util::tuple(type_Double, col_ndx, ndx, value)); // Throws + append_simple_instr(instr_Set, type_Double, col_ndx, ndx, value); // Throws return true; } @@ -1223,9 +1250,9 @@ inline bool TransactLogEncoder::set_string(size_t col_ndx, size_t ndx, StringDat } else { if (REALM_UNLIKELY(variant == instr_SetUnique)) - append_string_instr(variant, util::tuple(type_String, col_ndx, ndx, prior_num_rows), value); // Throws + append_simple_instr(variant, type_String, col_ndx, ndx, prior_num_rows, value); // Throws else - append_string_instr(variant, util::tuple(type_String, col_ndx, ndx), value); // Throws + append_simple_instr(variant, type_String, col_ndx, ndx, value); // Throws } return true; } @@ -1246,7 +1273,7 @@ inline bool TransactLogEncoder::set_binary(size_t col_ndx, size_t row_ndx, Binar } else { StringData value_2(value.data(), value.size()); - append_string_instr(variant, util::tuple(type_Binary, col_ndx, row_ndx), value_2); // Throws + append_simple_instr(variant, type_Binary, col_ndx, row_ndx, value_2); // Throws } return true; } @@ -1261,7 +1288,7 @@ inline void TransactLogConvenientEncoder::set_binary(const Table* t, size_t col_ inline bool TransactLogEncoder::set_olddatetime(size_t col_ndx, size_t ndx, OldDateTime value, Instruction variant) { REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant); - append_simple_instr(variant, util::tuple(type_OldDateTime, col_ndx, ndx, value.get_olddatetime())); // Throws + append_simple_instr(variant, type_OldDateTime, col_ndx, ndx, value.get_olddatetime()); // Throws return true; } @@ -1275,8 +1302,8 @@ inline void TransactLogConvenientEncoder::set_olddatetime(const Table* t, size_t inline bool TransactLogEncoder::set_timestamp(size_t col_ndx, size_t ndx, Timestamp value, Instruction variant) { REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant); - append_simple_instr( - variant, util::tuple(type_Timestamp, col_ndx, ndx, value.get_seconds(), value.get_nanoseconds())); // Throws + append_simple_instr(variant, type_Timestamp, col_ndx, ndx, value.get_seconds(), + value.get_nanoseconds()); // Throws return true; } @@ -1290,7 +1317,7 @@ inline void TransactLogConvenientEncoder::set_timestamp(const Table* t, size_t c inline bool TransactLogEncoder::set_table(size_t col_ndx, size_t ndx, Instruction variant) { REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant); - append_simple_instr(variant, util::tuple(type_Table, col_ndx, ndx)); // Throws + append_simple_instr(variant, type_Table, col_ndx, ndx); // Throws return true; } @@ -1303,7 +1330,7 @@ inline void TransactLogConvenientEncoder::set_table(const Table* t, size_t col_n inline bool TransactLogEncoder::set_mixed(size_t col_ndx, size_t ndx, const Mixed& value, Instruction variant) { REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant); - append_mixed_instr(variant, util::tuple(type_Mixed, col_ndx, ndx), value); // Throws + append_mixed_instr(variant, value, type_Mixed, col_ndx, ndx); // Throws return true; } @@ -1321,7 +1348,7 @@ inline bool TransactLogEncoder::set_link(size_t col_ndx, size_t ndx, size_t valu // Map `realm::npos` to zero, and `n` to `n+1`, where `n` is a target row // index. size_t value_2 = size_t(1) + value; - append_simple_instr(variant, util::tuple(type_Link, col_ndx, ndx, value_2, target_group_level_ndx)); // Throws + append_simple_instr(variant, type_Link, col_ndx, ndx, value_2, target_group_level_ndx); // Throws return true; } @@ -1337,10 +1364,10 @@ inline bool TransactLogEncoder::set_null(size_t col_ndx, size_t ndx, Instruction { REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault || variant == instr_SetUnique, variant); if (REALM_UNLIKELY(variant == instr_SetUnique)) { - append_simple_instr(variant, util::tuple(set_null_sentinel(), col_ndx, ndx, prior_num_rows)); // Throws + append_simple_instr(variant, set_null_sentinel(), col_ndx, ndx, prior_num_rows); // Throws } else { - append_simple_instr(variant, util::tuple(set_null_sentinel(), col_ndx, ndx)); // Throws + append_simple_instr(variant, set_null_sentinel(), col_ndx, ndx); // Throws } return true; } @@ -1355,7 +1382,7 @@ inline void TransactLogConvenientEncoder::set_null(const Table* t, size_t col_nd inline bool TransactLogEncoder::nullify_link(size_t col_ndx, size_t ndx, size_t target_group_level_ndx) { - append_simple_instr(instr_NullifyLink, util::tuple(col_ndx, ndx, target_group_level_ndx)); // Throws + append_simple_instr(instr_NullifyLink, col_ndx, ndx, target_group_level_ndx); // Throws return true; } @@ -1368,7 +1395,7 @@ inline void TransactLogConvenientEncoder::nullify_link(const Table* t, size_t co inline bool TransactLogEncoder::insert_substring(size_t col_ndx, size_t row_ndx, size_t pos, StringData value) { - append_string_instr(instr_InsertSubstring, util::tuple(col_ndx, row_ndx, pos), value); // Throws + append_simple_instr(instr_InsertSubstring, col_ndx, row_ndx, pos, value); // Throws return true; } @@ -1383,7 +1410,7 @@ inline void TransactLogConvenientEncoder::insert_substring(const Table* t, size_ inline bool TransactLogEncoder::erase_substring(size_t col_ndx, size_t row_ndx, size_t pos, size_t size) { - append_simple_instr(instr_EraseFromString, util::tuple(col_ndx, row_ndx, pos, size)); // Throws + append_simple_instr(instr_EraseFromString, col_ndx, row_ndx, pos, size); // Throws return true; } @@ -1399,8 +1426,7 @@ inline void TransactLogConvenientEncoder::erase_substring(const Table* t, size_t inline bool TransactLogEncoder::insert_empty_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, bool unordered) { - append_simple_instr(instr_InsertEmptyRows, - util::tuple(row_ndx, num_rows_to_insert, prior_num_rows, unordered)); // Throws + append_simple_instr(instr_InsertEmptyRows, row_ndx, num_rows_to_insert, prior_num_rows, unordered); // Throws return true; } @@ -1412,11 +1438,24 @@ inline void TransactLogConvenientEncoder::insert_empty_rows(const Table* t, size m_encoder.insert_empty_rows(row_ndx, num_rows_to_insert, prior_num_rows, unordered); // Throws } +inline bool TransactLogEncoder::add_row_with_key(size_t row_ndx, size_t prior_num_rows, size_t key_col_ndx, + int64_t key) +{ + append_simple_instr(instr_AddRowWithKey, row_ndx, prior_num_rows, key_col_ndx, key); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::add_row_with_key(const Table* t, size_t row_ndx, size_t prior_num_rows, + size_t key_col_ndx, int64_t key) +{ + select_table(t); // Throws + m_encoder.add_row_with_key(row_ndx, prior_num_rows, key_col_ndx, key); // Throws +} + inline bool TransactLogEncoder::erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, bool unordered) { - append_simple_instr(instr_EraseRows, util::tuple(row_ndx, num_rows_to_erase, prior_num_rows, - unordered)); // Throws + append_simple_instr(instr_EraseRows, row_ndx, num_rows_to_erase, prior_num_rows, unordered); // Throws return true; } @@ -1431,7 +1470,7 @@ inline void TransactLogConvenientEncoder::erase_rows(const Table* t, size_t row_ inline bool TransactLogEncoder::swap_rows(size_t row_ndx_1, size_t row_ndx_2) { - append_simple_instr(instr_SwapRows, util::tuple(row_ndx_1, row_ndx_2)); // Throws + append_simple_instr(instr_SwapRows, row_ndx_1, row_ndx_2); // Throws return true; } @@ -1442,9 +1481,22 @@ inline void TransactLogConvenientEncoder::swap_rows(const Table* t, size_t row_n m_encoder.swap_rows(row_ndx_1, row_ndx_2); } +inline bool TransactLogEncoder::move_row(size_t from_ndx, size_t to_ndx) +{ + append_simple_instr(instr_MoveRow, from_ndx, to_ndx); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::move_row(const Table* t, size_t from_ndx, size_t to_ndx) +{ + REALM_ASSERT(from_ndx != to_ndx); + select_table(t); // Throws + m_encoder.move_row(from_ndx, to_ndx); +} + inline bool TransactLogEncoder::merge_rows(size_t row_ndx, size_t new_row_ndx) { - append_simple_instr(instr_MergeRows, util::tuple(row_ndx, new_row_ndx)); // Throws + append_simple_instr(instr_MergeRows, row_ndx, new_row_ndx); // Throws return true; } @@ -1456,32 +1508,32 @@ inline void TransactLogConvenientEncoder::merge_rows(const Table* t, size_t row_ inline bool TransactLogEncoder::add_search_index(size_t col_ndx) { - append_simple_instr(instr_AddSearchIndex, util::tuple(col_ndx)); // Throws + append_simple_instr(instr_AddSearchIndex, col_ndx); // Throws return true; } -inline void TransactLogConvenientEncoder::add_search_index(const Table* t, size_t col_ndx) +inline void TransactLogConvenientEncoder::add_search_index(const Descriptor& desc, size_t col_ndx) { - select_table(t); // Throws + select_desc(desc); // Throws m_encoder.add_search_index(col_ndx); // Throws } inline bool TransactLogEncoder::remove_search_index(size_t col_ndx) { - append_simple_instr(instr_RemoveSearchIndex, util::tuple(col_ndx)); // Throws + append_simple_instr(instr_RemoveSearchIndex, col_ndx); // Throws return true; } -inline void TransactLogConvenientEncoder::remove_search_index(const Table* t, size_t col_ndx) +inline void TransactLogConvenientEncoder::remove_search_index(const Descriptor& desc, size_t col_ndx) { - select_table(t); // Throws + select_desc(desc); // Throws m_encoder.remove_search_index(col_ndx); // Throws } inline bool TransactLogEncoder::set_link_type(size_t col_ndx, LinkType link_type) { - append_simple_instr(instr_SetLinkType, util::tuple(col_ndx, int(link_type))); // Throws + append_simple_instr(instr_SetLinkType, col_ndx, int(link_type)); // Throws return true; } @@ -1492,21 +1544,21 @@ inline void TransactLogConvenientEncoder::set_link_type(const Table* t, size_t c } -inline bool TransactLogEncoder::clear_table() +inline bool TransactLogEncoder::clear_table(size_t old_size) { - append_simple_instr(instr_ClearTable, util::tuple()); // Throws + append_simple_instr(instr_ClearTable, old_size); // Throws return true; } -inline void TransactLogConvenientEncoder::clear_table(const Table* t) +inline void TransactLogConvenientEncoder::clear_table(const Table* t, size_t prior_num_rows) { select_table(t); // Throws - m_encoder.clear_table(); // Throws + m_encoder.clear_table(prior_num_rows); // Throws } inline bool TransactLogEncoder::optimize_table() { - append_simple_instr(instr_OptimizeTable, util::tuple()); // Throws + append_simple_instr(instr_OptimizeTable); // Throws return true; } @@ -1518,7 +1570,7 @@ inline void TransactLogConvenientEncoder::optimize_table(const Table* t) inline bool TransactLogEncoder::link_list_set(size_t link_ndx, size_t value, size_t prior_size) { - append_simple_instr(instr_LinkListSet, util::tuple(link_ndx, value, prior_size)); // Throws + append_simple_instr(instr_LinkListSet, link_ndx, value, prior_size); // Throws return true; } @@ -1530,7 +1582,7 @@ inline void TransactLogConvenientEncoder::link_list_set(const LinkView& list, si inline bool TransactLogEncoder::link_list_nullify(size_t link_ndx, size_t prior_size) { - append_simple_instr(instr_LinkListNullify, util::tuple(link_ndx, prior_size)); // Throws + append_simple_instr(instr_LinkListNullify, link_ndx, prior_size); // Throws return true; } @@ -1543,45 +1595,10 @@ inline void TransactLogConvenientEncoder::link_list_nullify(const LinkView& list inline bool TransactLogEncoder::link_list_set_all(const IntegerColumn& values) { - struct iter { - iter(const IntegerColumn& iter_values, size_t ndx) - : m_values(&iter_values) - , m_ndx(ndx) - { - } - const IntegerColumn* m_values; - size_t m_ndx; - bool operator==(const iter& i) const - { - return m_ndx == i.m_ndx; - } - bool operator!=(const iter& i) const - { - return m_ndx != i.m_ndx; - } - size_t operator-(const iter& i) const - { - return m_ndx - i.m_ndx; - } - int_fast64_t operator*() const - { - return m_values->get(m_ndx); - } - iter& operator++() - { - ++m_ndx; - return *this; - } - iter operator++(int) - { - iter i = *this; - ++m_ndx; - return i; - } - }; size_t num_values = values.size(); - append_variable_size_instr(instr_LinkListSetAll, util::tuple(num_values), iter(values, 0), - iter(values, num_values)); // Throws + append_simple_instr( + instr_LinkListSetAll, num_values, + std::make_tuple(IntegerColumnIterator(&values, 0), IntegerColumnIterator(&values, num_values))); // Throws return true; } @@ -1593,7 +1610,7 @@ inline void TransactLogConvenientEncoder::set_link_list(const LinkView& list, co inline bool TransactLogEncoder::link_list_insert(size_t link_ndx, size_t value, size_t prior_size) { - append_simple_instr(instr_LinkListInsert, util::tuple(link_ndx, value, prior_size)); // Throws + append_simple_instr(instr_LinkListInsert, link_ndx, value, prior_size); // Throws return true; } @@ -1607,7 +1624,7 @@ inline void TransactLogConvenientEncoder::link_list_insert(const LinkView& list, inline bool TransactLogEncoder::link_list_move(size_t from_link_ndx, size_t to_link_ndx) { REALM_ASSERT(from_link_ndx != to_link_ndx); - append_simple_instr(instr_LinkListMove, util::tuple(from_link_ndx, to_link_ndx)); // Throws + append_simple_instr(instr_LinkListMove, from_link_ndx, to_link_ndx); // Throws return true; } @@ -1620,7 +1637,7 @@ inline void TransactLogConvenientEncoder::link_list_move(const LinkView& list, s inline bool TransactLogEncoder::link_list_swap(size_t link1_ndx, size_t link2_ndx) { - append_simple_instr(instr_LinkListSwap, util::tuple(link1_ndx, link2_ndx)); // Throws + append_simple_instr(instr_LinkListSwap, link1_ndx, link2_ndx); // Throws return true; } @@ -1632,7 +1649,7 @@ inline void TransactLogConvenientEncoder::link_list_swap(const LinkView& list, s inline bool TransactLogEncoder::link_list_erase(size_t link_ndx, size_t prior_size) { - append_simple_instr(instr_LinkListErase, util::tuple(link_ndx, prior_size)); // Throws + append_simple_instr(instr_LinkListErase, link_ndx, prior_size); // Throws return true; } @@ -1645,7 +1662,7 @@ inline void TransactLogConvenientEncoder::link_list_erase(const LinkView& list, inline bool TransactLogEncoder::link_list_clear(size_t old_list_size) { - append_simple_instr(instr_LinkListClear, util::tuple(old_list_size)); // Throws + append_simple_instr(instr_LinkListClear, old_list_size); // Throws return true; } @@ -1856,6 +1873,15 @@ void TransactLogParser::parse_one(InstructionHandler& handler) parser_error(); return; } + case instr_AddRowWithKey: { + size_t row_ndx = read_int(); // Throws + size_t prior_num_rows = read_int(); // Throws + size_t key_col_ndx = read_int(); // Throws + int64_t key = read_int(); // Throws + if (!handler.add_row_with_key(row_ndx, prior_num_rows, key_col_ndx, key)) // Throws + parser_error(); + return; + } case instr_EraseRows: { size_t row_ndx = read_int(); // Throws size_t num_rows_to_erase = read_int(); // Throws @@ -1872,6 +1898,13 @@ void TransactLogParser::parse_one(InstructionHandler& handler) parser_error(); return; } + case instr_MoveRow: { + size_t from_ndx = read_int(); // Throws + size_t to_ndx = read_int(); // Throws + if (!handler.move_row(from_ndx, to_ndx)) // Throws + parser_error(); + return; + } case instr_MergeRows: { size_t row_ndx = read_int(); // Throws size_t new_row_ndx = read_int(); // Throws @@ -1897,7 +1930,8 @@ void TransactLogParser::parse_one(InstructionHandler& handler) return; } case instr_ClearTable: { - if (!handler.clear_table()) // Throws + size_t old_size = read_int(); // Throws + if (!handler.clear_table(old_size)) // Throws parser_error(); return; } @@ -1969,6 +2003,13 @@ void TransactLogParser::parse_one(InstructionHandler& handler) parser_error(); return; } + case instr_MoveColumn: { + // FIXME: remove this in the next breaking change. + // This instruction is no longer supported and not used by either + // bindings or sync, so if we see it here, there was a problem parsing. + parser_error(); + return; + } case instr_AddSearchIndex: { size_t col_ndx = read_int(); // Throws if (!handler.add_search_index(col_ndx)) // Throws @@ -2000,8 +2041,8 @@ void TransactLogParser::parse_one(InstructionHandler& handler) parser_error(); StringData name = read_string(m_string_buffer); // Throws bool nullable = (Instruction(instr) == instr_InsertNullableColumn); - if (REALM_UNLIKELY(nullable && (type == type_Table || type == type_Mixed))) { - // Nullability not supported for Table and Mixed columns. + if (REALM_UNLIKELY(nullable && (type == type_Mixed))) { + // Nullability not supported for Mixed columns. parser_error(); } if (!handler.insert_column(col_ndx, DataType(type), name, nullable)) // Throws @@ -2044,13 +2085,6 @@ void TransactLogParser::parse_one(InstructionHandler& handler) parser_error(); return; } - case instr_MoveColumn: { - size_t col_ndx_1 = read_int(); // Throws - size_t col_ndx_2 = read_int(); // Throws - if (!handler.move_column(col_ndx_1, col_ndx_2)) // Throws - parser_error(); - return; - } case instr_SelectDescriptor: { int levels = read_int(); // Throws if (levels < 0 || levels > m_max_levels) @@ -2088,10 +2122,10 @@ void TransactLogParser::parse_one(InstructionHandler& handler) return; } case instr_MoveGroupLevelTable: { - size_t from_table_ndx = read_int(); // Throws - size_t to_table_ndx = read_int(); // Throws - if (!handler.move_group_level_table(from_table_ndx, to_table_ndx)) // Throws - parser_error(); + // This instruction is no longer supported and not used by either + // bindings or sync, so if we see it here, there was a problem parsing. + // FIXME: remove this in the next breaking change. + parser_error(); return; } case instr_OptimizeTable: { @@ -2153,14 +2187,14 @@ inline void TransactLogParser::read_bytes(char* data, size_t size) const size_t avail = m_input_end - m_input_begin; if (size <= avail) break; - std::copy_n(m_input_begin, avail, data); + realm::safe_copy_n(m_input_begin, avail, data); if (!next_input_buffer()) throw BadTransactLog(); data += avail; size -= avail; } const char* to = m_input_begin + size; - std::copy_n(m_input_begin, size, data); + realm::safe_copy_n(m_input_begin, size, data); m_input_begin = to; } @@ -2221,8 +2255,9 @@ inline StringData TransactLogParser::read_string(util::StringBuffer& buf) inline Timestamp TransactLogParser::read_timestamp() { - REALM_ASSERT(false); - return Timestamp{}; + int64_t seconds = read_int(); // Throws + int32_t nanoseconds = read_int(); // Throws + return Timestamp(seconds, nanoseconds); } @@ -2230,9 +2265,6 @@ inline BinaryData TransactLogParser::read_binary(util::StringBuffer& buf) { size_t size = read_int(); // Throws - if (size > ArrayBlob::max_binary_size) - parser_error(); - return read_buffer(buf, size); } @@ -2383,14 +2415,6 @@ class TransactReverser { return true; } - bool move_group_level_table(size_t from_table_ndx, size_t to_table_ndx) - { - sync_table(); - m_encoder.move_group_level_table(to_table_ndx, from_table_ndx); - append_instruction(); - return true; - } - bool optimize_table() { return true; // No-op @@ -2405,6 +2429,14 @@ class TransactReverser { return true; } + bool add_row_with_key(size_t row_ndx, size_t prior_num_rows, size_t, int64_t) + { + bool unordered = true; + m_encoder.erase_rows(row_ndx, 1, prior_num_rows + 1, unordered); // Throws + append_instruction(); + return true; + } + bool erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, bool unordered) { size_t num_rows_to_insert = num_rows_to_erase; @@ -2422,11 +2454,23 @@ class TransactReverser { return true; } + bool move_row(size_t from_ndx, size_t to_ndx) + { + m_encoder.move_row(to_ndx, from_ndx); + append_instruction(); + return true; + } + bool merge_rows(size_t row_ndx, size_t new_row_ndx) { - static_cast(row_ndx); - static_cast(new_row_ndx); // There is no instruction we can generate here to change back. + // However, we do need to refresh accessors for any tables + // connected through backlinks, so we generate updates on each + // affected row by merging to itself. + m_encoder.merge_rows(row_ndx, row_ndx); + append_instruction(); + m_encoder.merge_rows(new_row_ndx, new_row_ndx); + append_instruction(); return true; } @@ -2531,10 +2575,10 @@ class TransactReverser { return true; // No-op } - bool clear_table() + bool clear_table(size_t old_size) { - // FIXME: Add a comment on why we call insert_empty_rows() inside clear_table() - m_encoder.insert_empty_rows(0, 0, 0, true); + bool unordered = false; + m_encoder.insert_empty_rows(0, old_size, 0, unordered); append_instruction(); return true; } @@ -2589,13 +2633,6 @@ class TransactReverser { return true; // No-op } - bool move_column(size_t col_ndx_1, size_t col_ndx_2) - { - m_encoder.move_column(col_ndx_2, col_ndx_1); - append_instruction(); - return true; - } - bool select_link_list(size_t col_ndx, size_t row_ndx, size_t link_target_group_level_ndx) { sync_linkview(); diff --git a/Pods/Realm/include/core/realm/importer.hpp b/Pods/Realm/include/core/realm/importer.hpp deleted file mode 100644 index 9ef54dc..0000000 --- a/Pods/Realm/include/core/realm/importer.hpp +++ /dev/null @@ -1,131 +0,0 @@ -/************************************************************************* - * - * Copyright 2016 Realm Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - **************************************************************************/ - -#ifndef REALM_IMPORTER_HPP -#define REALM_IMPORTER_HPP - -/* -Main method: import_csv(). Arguments: ---------------------------------------------------------------------------------------------------------------------- -empty_as_string_flag: - Imports a column that has occurences of empty strings as String type column. Else fields arec onverted to - false/0/0.0 - -type_detection_rows: - tells how many rows to read before analyzing data types (to see if numeric rows are really - numeric everywhere, and not strings that happen to just mostly contain numeric characters - - -This library supports: ---------------------------------------------------------------------------------------------------------------------- - * Auto detection of float vs. double, depending on number of significant digits - * Bool types can be case insensitive "true, false, 0, 1, yes, no" - * Newline inside data fields, plus auto detection of non-conforming non-quoted newlines (as in some IBM sample - files) - * Realm types String, Integer, Bool, Float and Double - * Auto detection of header and naming of Realm columns accordingly - * double-quoted and non-quoted fields, and these can be mixed arbitrarely - * double-quotes inside data field - * *nix + MacOSv9 + Windows line feed - * Scientific notation of floats/doubles (+1.23e-10) - * Comma in floats - but ONLY if field is double-quoted - * FAST FAST FAST (200 MB/s). Uses state-machine instead of traditional char-by-char loop with state checks inside - - -Problems: ---------------------------------------------------------------------------------------------------------------------- - A csv file does not tell its sheme. So we auto-detect it, based on the first N rows. However if a given column - contains 'false, false, false, hello' and we detect and create Realm table scheme using the first 3 rows, we fail - when we meet 'hello' (this error is handled with a thorough error message) - - Does not support commas in floats unless field is double-quoted - - -Design: ---------------------------------------------------------------------------------------------------------------------- - -import_csv(csv file handle, realm table) - Calls tokenize(csv file handle): - reads payload chunk and returns std::vector> with the right dimensions filled with - rows and columns of the chunk payload - Calls parse_float(), parse_bool(), etc, which tests for type and returns converted values - Calls table.add_empty_row(), table.set_float(), table.set_bool() -*/ - -#include - -// Disk read chunk size. This MUST be large enough to contain at least TWO rows of csv plaintext! It's a good idea -// to set it as low as ever possible (like 32 K) even though it's counter-intuitive with respect to performance. It -// will make the operating system read 32 K from disk and return it, and then read-ahead 32-64 K more after fread() -// has returned. This read-ahead behaviour does NOT occur if we request megabyte-sized chunks (observed on Windows 7 / -// Ubuntu) -static const size_t chunk_size = 32 * 1024; - -// Number of rows to csv-parse + insert into realm in each iteration. -static const size_t record_chunks = 100; - -// Width of each column when printing them on screen (non-Quiet mode) -const size_t print_width = 25; - -#include -#include - -namespace realm { - -class Importer { -public: - Importer(); - size_t import_csv_auto(FILE* file, Table& table, size_t type_detection_rows = 1000, - size_t import_rows = static_cast(-1)); - - size_t import_csv_manual(FILE* file, Table& table, std::vector scheme, - std::vector column_names, size_t skip_first_rows = 0, - size_t import_rows = static_cast(-1)); - - bool Quiet; // Quiet mode, only print to screen upon errors - char Separator; // csv delimitor/separator - bool Empty_as_string; // Import columns that have occurences of empty strings as String type column - -private: - size_t import_csv(FILE* file, Table& table, std::vector* import_scheme, - std::vector* column_names, size_t type_detection_rows, size_t skip_first_rows, - size_t import_rows); - template - float parse_float(const char* col, bool* success = nullptr); - template - double parse_double(const char* col, bool* success = nullptr, size_t* significants = nullptr); - template - int64_t parse_integer(const char* col, bool* success = nullptr); - template - bool parse_bool(const char* col, bool* success = nullptr); - std::vector types(std::vector v); - size_t tokenize(std::vector>& payload, size_t records); - std::vector detect_scheme(std::vector> payload, size_t begin, size_t end); - std::vector lowest_common(std::vector types1, std::vector types2); - - char src[2 * chunk_size]; // .csv input buffer - size_t m_top; // points at top of buffer - size_t m_curpos; // points at next byte to parse - FILE* m_file; // handle to .csv file - size_t m_fields; // number of fields in each row - size_t m_row; // current row in .csv file, including field-embedded line breaks. Used for err msg only -}; - -} // namespace realm - -#endif // REALM_IMPORTER_HPP diff --git a/Pods/Realm/include/core/realm/index_string.hpp b/Pods/Realm/include/core/realm/index_string.hpp index 76cb68c..b357c19 100644 --- a/Pods/Realm/include/core/realm/index_string.hpp +++ b/Pods/Realm/include/core/realm/index_string.hpp @@ -75,29 +75,39 @@ class IndexArray : public Array { } size_t index_string_find_first(StringData value, ColumnBase* column) const; - void index_string_find_all(IntegerColumn& result, StringData value, ColumnBase* column) const; + void index_string_find_all(IntegerColumn& result, StringData value, ColumnBase* column, bool case_insensitive = false) const; FindRes index_string_find_all_no_copy(StringData value, ColumnBase* column, InternalFindResult& result) const; size_t index_string_count(StringData value, ColumnBase* column) const; private: template - size_t from_list(StringData value, IntegerColumn& result, InternalFindResult& result_ref, - const IntegerColumn& rows, ColumnBase* column) const; + size_t from_list(StringData value, InternalFindResult& result_ref, const IntegerColumn& rows, + ColumnBase* column) const; - template - size_t index_string(StringData value, IntegerColumn& result, InternalFindResult& result_ref, - ColumnBase* column) const; + void from_list_all(StringData value, IntegerColumn& result, const IntegerColumn& rows, ColumnBase* column) const; + + void from_list_all_ins(StringData value, std::vector& result, const IntegerColumn& rows, + ColumnBase* column) const; + + template + size_t index_string(StringData value, InternalFindResult& result_ref, ColumnBase* column) const; + + void index_string_all(StringData value, IntegerColumn& result, ColumnBase* column) const; + + void index_string_all_ins(StringData value, IntegerColumn& result, ColumnBase* column) const; }; class StringIndex { public: StringIndex(ColumnBase* target_column, Allocator&); - StringIndex(ref_type, ArrayParent*, size_t ndx_in_parent, ColumnBase* target_column, bool allow_duplicate_values, - Allocator&); + StringIndex(ref_type, ArrayParent*, size_t ndx_in_parent, ColumnBase* target_column, Allocator&); ~StringIndex() noexcept { } + + static ref_type create_empty(Allocator& alloc); + void set_target(ColumnBase* target_column) noexcept; // Accessor concept: @@ -136,7 +146,7 @@ class StringIndex { template size_t find_first(T value) const; template - void find_all(IntegerColumn& result, T value) const; + void find_all(IntegerColumn& result, T value, bool case_insensitive = false) const; template FindRes find_all_no_copy(T value, InternalFindResult& result) const; template @@ -149,15 +159,14 @@ class StringIndex { void distinct(IntegerColumn& result) const; bool has_duplicate_values() const noexcept; - /// By default, duplicate values are allowed. - void set_allow_duplicate_values(bool) noexcept; - void verify() const; #ifdef REALM_DEBUG - void verify_entries(const StringColumn& column) const; + template + void verify_entries(const T& column) const; void do_dump_node_structure(std::ostream&, int) const; void to_dot() const; void to_dot(std::ostream&, StringData title = StringData()) const; + void to_dot_2(std::ostream&, StringData title = StringData()) const; #endif typedef int32_t key_type; @@ -196,7 +205,6 @@ class StringIndex { // If the header flag is set, references point to a sub-StringIndex (nesting). std::unique_ptr m_array; ColumnBase* m_target_column; - bool m_deny_duplicate_values; struct inner_node_tag { }; @@ -249,7 +257,6 @@ class StringIndex { #ifdef REALM_DEBUG static void dump_node_structure(const Array& node, std::ostream&, int level); - void to_dot_2(std::ostream&, StringData title = StringData()) const; static void array_to_dot(std::ostream&, const Array&); static void keys_to_dot(std::ostream&, const Array&, StringData title = StringData()); #endif @@ -277,7 +284,7 @@ struct GetIndexData { static StringData get_index_data(const int64_t& value, StringIndex::StringConversionBuffer& buffer) { const char* c = reinterpret_cast(&value); - std::copy_n(c, sizeof(int64_t), buffer.data()); + realm::safe_copy_n(c, sizeof(int64_t), buffer.data()); return StringData{buffer.data(), sizeof(int64_t)}; } }; @@ -346,15 +353,13 @@ inline StringData to_str(T&& value, StringIndex::StringConversionBuffer& buffer) inline StringIndex::StringIndex(ColumnBase* target_column, Allocator& alloc) : m_array(create_node(alloc, true)) // Throws , m_target_column(target_column) - , m_deny_duplicate_values(false) { } inline StringIndex::StringIndex(ref_type ref, ArrayParent* parent, size_t ndx_in_parent, ColumnBase* target_column, - bool deny_duplicate_values, Allocator& alloc) + Allocator& alloc) : m_array(new IndexArray(alloc)) , m_target_column(target_column) - , m_deny_duplicate_values(deny_duplicate_values) { REALM_ASSERT_EX(Array::get_context_flag_from_header(alloc.translate(ref)), ref, size_t(alloc.translate(ref))); m_array->init_from_ref(ref); @@ -364,13 +369,7 @@ inline StringIndex::StringIndex(ref_type ref, ArrayParent* parent, size_t ndx_in inline StringIndex::StringIndex(inner_node_tag, Allocator& alloc) : m_array(create_node(alloc, false)) // Throws , m_target_column(nullptr) - , m_deny_duplicate_values(false) -{ -} - -inline void StringIndex::set_allow_duplicate_values(bool allow) noexcept { - m_deny_duplicate_values = !allow; } // Byte order of the key is *reversed*, so that for the integer index, the least significant @@ -532,11 +531,11 @@ size_t StringIndex::find_first(T value) const } template -void StringIndex::find_all(IntegerColumn& result, T value) const +void StringIndex::find_all(IntegerColumn& result, T value, bool case_insensitive) const { // Use direct access method StringConversionBuffer buffer; - return m_array->index_string_find_all(result, to_str(value, buffer), m_target_column); + return m_array->index_string_find_all(result, to_str(value, buffer), m_target_column, case_insensitive); } template diff --git a/Pods/Realm/include/core/realm/lang_bind_helper.hpp b/Pods/Realm/include/core/realm/lang_bind_helper.hpp index 9e93c77..de287bf 100644 --- a/Pods/Realm/include/core/realm/lang_bind_helper.hpp +++ b/Pods/Realm/include/core/realm/lang_bind_helper.hpp @@ -218,16 +218,14 @@ inline Table* LangBindHelper::copy_table(const Table& table) inline Table* LangBindHelper::get_subtable_ptr(Table* t, size_t column_ndx, size_t row_ndx) { - Table* subtab = t->get_subtable_ptr(column_ndx, row_ndx); // Throws - subtab->bind_ptr(); - return subtab; + TableRef subtab = t->get_subtable_tableref(column_ndx, row_ndx); // Throws + return subtab.release(); } inline const Table* LangBindHelper::get_subtable_ptr(const Table* t, size_t column_ndx, size_t row_ndx) { - const Table* subtab = t->get_subtable_ptr(column_ndx, row_ndx); // Throws - subtab->bind_ptr(); - return subtab; + ConstTableRef subtab = t->get_subtable_tableref(column_ndx, row_ndx); // Throws + return subtab.release(); } inline Table* LangBindHelper::get_subtable_ptr(TableView* tv, size_t column_ndx, size_t row_ndx) diff --git a/Pods/Realm/include/core/realm/link_view.hpp b/Pods/Realm/include/core/realm/link_view.hpp index c36a3c8..14be525 100644 --- a/Pods/Realm/include/core/realm/link_view.hpp +++ b/Pods/Realm/include/core/realm/link_view.hpp @@ -78,7 +78,7 @@ class LinkView : public RowIndexes, public std::enable_shared_from_this + +#include +#include +#include + +#if REALM_METRICS + +namespace realm { +namespace metrics { + + +class MetricTimerResult +{ +public: + MetricTimerResult(); + ~MetricTimerResult(); + double get_elapsed_seconds() const; + void report_seconds(double time); +protected: + double m_elapsed_seconds; +}; + + +class MetricTimer { +public: + MetricTimer(std::shared_ptr destination = nullptr); + ~MetricTimer(); + + void reset(); + + /// Returns elapsed time in seconds since last call to reset(). + double get_elapsed_time() const; + /// Same as get_elapsed_time(). + operator double() const; + + /// Format the elapsed time on the form 0h00m, 00m00s, 00.00s, or + /// 000.0ms depending on magnitude. + static void format(double seconds, std::ostream&); + + static std::string format(double seconds); + +private: + using clock_type = std::chrono::high_resolution_clock; + using time_point = std::chrono::time_point; + time_point m_start; + time_point m_paused_at; + std::shared_ptr m_dest; + + time_point get_timer_ticks() const; + double calc_elapsed_seconds(time_point begin, time_point end) const; +}; + + +inline void MetricTimer::reset() +{ + m_start = get_timer_ticks(); +} + +inline double MetricTimer::get_elapsed_time() const +{ + return calc_elapsed_seconds(m_start, get_timer_ticks()); +} + +inline MetricTimer::operator double() const +{ + return get_elapsed_time(); +} + +inline std::ostream& operator<<(std::ostream& out, const MetricTimer& timer) +{ + MetricTimer::format(timer, out); + return out; +} + + +} // namespace metrics +} // namespace realm + +#endif // REALM_METRICS + +#endif // REALM_METRIC_TIMER_HPP diff --git a/Pods/Realm/include/core/realm/metrics/metrics.hpp b/Pods/Realm/include/core/realm/metrics/metrics.hpp new file mode 100644 index 0000000..73e8d59 --- /dev/null +++ b/Pods/Realm/include/core/realm/metrics/metrics.hpp @@ -0,0 +1,82 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_METRICS_HPP +#define REALM_METRICS_HPP + +#include +#include + +#include +#include +#include + +namespace realm { + +class Group; + +namespace metrics { + +#if REALM_METRICS + +class Metrics { +public: + Metrics(); + ~Metrics() noexcept; + size_t num_query_metrics() const; + size_t num_transaction_metrics() const; + + void add_query(QueryInfo info); + void add_transaction(TransactionInfo info); + + void start_read_transaction(); + void start_write_transaction(); + void end_read_transaction(size_t total_size, size_t free_space, size_t num_objects, size_t num_versions); + void end_write_transaction(size_t total_size, size_t free_space, size_t num_objects, size_t num_versions); + static std::unique_ptr report_fsync_time(const Group& g); + static std::unique_ptr report_write_time(const Group& g); + + using QueryInfoList = std::vector; + using TransactionInfoList = std::vector; + + // Get the list of metric objects tracked since the last take + std::unique_ptr take_queries(); + std::unique_ptr take_transactions(); +private: + std::unique_ptr m_query_info; + std::unique_ptr m_transaction_info; + + std::unique_ptr m_pending_read; + std::unique_ptr m_pending_write; +}; + + +#else + +class Metrics +{ +}; + +#endif // REALM_METRICS + +} // namespace metrics +} // namespace realm + + + +#endif // REALM_METRICS_HPP diff --git a/Pods/Realm/include/core/realm/metrics/query_info.hpp b/Pods/Realm/include/core/realm/metrics/query_info.hpp new file mode 100644 index 0000000..020eaa5 --- /dev/null +++ b/Pods/Realm/include/core/realm/metrics/query_info.hpp @@ -0,0 +1,72 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_QUERY_INFO_HPP +#define REALM_QUERY_INFO_HPP + +#include +#include +#include + +#include +#include +#include + +#if REALM_METRICS + +namespace realm { + +class Query; // forward declare in namespace realm + +namespace metrics { + +class QueryInfo { +public: + + enum QueryType { + type_Find, + type_FindAll, + type_Count, + type_Sum, + type_Average, + type_Maximum, + type_Minimum, + type_Invalid + }; + + QueryInfo(const Query* query, QueryType type); + ~QueryInfo() noexcept; + + std::string get_description() const; + QueryType get_type() const; + double get_query_time() const; + + static std::unique_ptr track(const Query* query, QueryType type); + static QueryType type_from_action(Action action); + +private: + std::string m_description; + QueryType m_type; + std::shared_ptr m_query_time; +}; + +} // namespace metrics +} // namespace realm + +#endif // REALM_METRICS +#endif // REALM_QUERY_INFO_HPP diff --git a/Pods/Realm/include/core/realm/metrics/transaction_info.hpp b/Pods/Realm/include/core/realm/metrics/transaction_info.hpp new file mode 100644 index 0000000..c647e34 --- /dev/null +++ b/Pods/Realm/include/core/realm/metrics/transaction_info.hpp @@ -0,0 +1,77 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_TRANSACTION_INFO_HPP +#define REALM_TRANSACTION_INFO_HPP + +#include +#include + +#include +#include + +#if REALM_METRICS + +namespace realm { +namespace metrics { + +class Metrics; + +class TransactionInfo { +public: + enum TransactionType { + read_transaction, + write_transaction + }; + TransactionInfo(TransactionType type); + TransactionInfo(const TransactionInfo&) = default; + ~TransactionInfo() noexcept; + + TransactionType get_transaction_type() const; + // the transaction time is a total amount which includes fsync_time + write_time + user_time + double get_transaction_time() const; + double get_fsync_time() const; + double get_write_time() const; + size_t get_disk_size() const; + size_t get_free_space() const; + size_t get_total_objects() const; + size_t get_num_available_versions() const; + +private: + MetricTimerResult m_transaction_time; + std::shared_ptr m_fsync_time; + std::shared_ptr m_write_time; + MetricTimer m_transact_timer; + + size_t m_realm_disk_size; + size_t m_realm_free_space; + size_t m_total_objects; + TransactionType m_type; + size_t m_num_versions; + + friend class Metrics; + void update_stats(size_t disk_size, size_t free_space, size_t total_objects, size_t available_versions); + void finish_timer(); +}; + +} // namespace metrics +} // namespace realm + +#endif // REALM_METRICS + +#endif // REALM_TRANSACTION_INFO_HPP diff --git a/Pods/Realm/include/core/realm/mixed.hpp b/Pods/Realm/include/core/realm/mixed.hpp index 8f07f4f..f50f4a0 100644 --- a/Pods/Realm/include/core/realm/mixed.hpp +++ b/Pods/Realm/include/core/realm/mixed.hpp @@ -24,14 +24,13 @@ #include // size_t #include -#include -#include -#include +#include #include #include #include -#include -#include +#include +#include +#include namespace realm { @@ -238,6 +237,11 @@ bool operator!=(Wrap, OldDateTime) noexcept; bool operator==(OldDateTime, Wrap) noexcept; bool operator!=(OldDateTime, Wrap) noexcept; +// Compare mixed with Timestamp +bool operator==(Wrap, Timestamp) noexcept; +bool operator!=(Wrap, Timestamp) noexcept; +bool operator==(Timestamp, Wrap) noexcept; +bool operator!=(Timestamp, Wrap) noexcept; // Implementation: @@ -394,14 +398,11 @@ inline void Mixed::set_olddatetime(OldDateTime v) noexcept m_date = v.get_olddatetime(); } -// LCOV_EXCL_START inline void Mixed::set_timestamp(Timestamp v) noexcept { - REALM_ASSERT(false && "not yet implemented"); m_type = type_Timestamp; m_timestamp = v; } -// LCOV_EXCL_STOP // LCOV_EXCL_START template @@ -651,6 +652,28 @@ inline bool operator!=(OldDateTime a, Wrap b) noexcept return type_OldDateTime != Mixed(b).get_type() || a != OldDateTime(Mixed(b).get_olddatetime()); } +// Compare mixed with Timestamp + +inline bool operator==(Wrap a, Timestamp b) noexcept +{ + return Mixed(a).get_type() == type_Timestamp && Timestamp(Mixed(a).get_timestamp()) == b; +} + +inline bool operator!=(Wrap a, Timestamp b) noexcept +{ + return Mixed(a).get_type() != type_Timestamp || Timestamp(Mixed(a).get_timestamp()) != b; +} + +inline bool operator==(Timestamp a, Wrap b) noexcept +{ + return type_Timestamp == Mixed(b).get_type() && a == Timestamp(Mixed(b).get_timestamp()); +} + +inline bool operator!=(Timestamp a, Wrap b) noexcept +{ + return type_Timestamp != Mixed(b).get_type() || a != Timestamp(Mixed(b).get_timestamp()); +} + } // namespace realm diff --git a/Pods/Realm/include/core/realm/null.hpp b/Pods/Realm/include/core/realm/null.hpp index be010b8..733e798 100644 --- a/Pods/Realm/include/core/realm/null.hpp +++ b/Pods/Realm/include/core/realm/null.hpp @@ -20,6 +20,7 @@ #define REALM_NULL_HPP #include +#include #include #include @@ -159,6 +160,13 @@ struct null { } }; +template +OS& operator<<(OS& os, const null&) +{ + os << "(null)"; + return os; +} + } // namespace realm #endif // REALM_NULL_HPP diff --git a/Pods/Realm/include/core/realm/owned_data.hpp b/Pods/Realm/include/core/realm/owned_data.hpp index 039ccc8..c707f9d 100644 --- a/Pods/Realm/include/core/realm/owned_data.hpp +++ b/Pods/Realm/include/core/realm/owned_data.hpp @@ -19,6 +19,8 @@ #ifndef REALM_OWNED_DATA_HPP #define REALM_OWNED_DATA_HPP +#include + #include #include diff --git a/Pods/Realm/include/core/realm/parser/collection_operator_expression.hpp b/Pods/Realm/include/core/realm/parser/collection_operator_expression.hpp new file mode 100644 index 0000000..4edd5c2 --- /dev/null +++ b/Pods/Realm/include/core/realm/parser/collection_operator_expression.hpp @@ -0,0 +1,226 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#ifndef REALM_COLLECTION_OPERATOR_EXPRESSION_HPP +#define REALM_COLLECTION_OPERATOR_EXPRESSION_HPP + +#include "property_expression.hpp" +#include "parser.hpp" +#include "parser_utils.hpp" + +#include + +namespace realm { +namespace parser { + +template +struct CollectionOperatorGetter; + +template +struct CollectionOperatorExpression +{ + static constexpr parser::Expression::KeyPathOp operation_type = OpType; + std::function
table_getter; + PropertyExpression pe; + size_t post_link_col_ndx; + DataType post_link_col_type; + + CollectionOperatorExpression(PropertyExpression&& exp, std::string suffix_path, parser::KeyPathMapping& mapping) + : pe(std::move(exp)) + , post_link_col_ndx(realm::not_found) + { + table_getter = std::bind(&PropertyExpression::table_getter, pe); + + const bool requires_suffix_path = !(OpType == parser::Expression::KeyPathOp::SizeString + || OpType == parser::Expression::KeyPathOp::SizeBinary + || OpType == parser::Expression::KeyPathOp::Count + || OpType == parser::Expression::KeyPathOp::BacklinkCount); + + if (requires_suffix_path) { + Table* pre_link_table = pe.table_getter(); + REALM_ASSERT(pre_link_table); + StringData list_property_name; + if (pe.dest_type_is_backlink()) { + list_property_name = "linking object"; + } else { + list_property_name = pre_link_table->get_column_name(pe.get_dest_ndx()); + } + realm_precondition(pe.get_dest_type() == type_LinkList || pe.dest_type_is_backlink(), + util::format("The '%1' operation must be used on a list property, but '%2' is not a list", + util::collection_operator_to_str(OpType), list_property_name)); + + ConstTableRef post_link_table; + if (pe.dest_type_is_backlink()) { + post_link_table = pe.get_dest_table(); + } else { + post_link_table = pe.get_dest_table()->get_link_target(pe.get_dest_ndx()); + } + REALM_ASSERT(post_link_table); + StringData printable_post_link_table_name = get_printable_table_name(*post_link_table); + + KeyPath suffix_key_path = key_path_from_string(suffix_path); + + realm_precondition(suffix_path.size() > 0 && suffix_key_path.size() > 0, + util::format("A property from object '%1' must be provided to perform operation '%2'", + printable_post_link_table_name, util::collection_operator_to_str(OpType))); + size_t index = 0; + KeyPathElement element = mapping.process_next_path(post_link_table, suffix_key_path, index); + + realm_precondition(suffix_key_path.size() == 1, + util::format("Unable to use '%1' because collection aggreate operations are only supported " + "for direct properties at this time", suffix_path)); + + post_link_col_ndx = element.col_ndx; + post_link_col_type = element.col_type; + } + else { // !requires_suffix_path + if (!pe.link_chain.empty()) { + post_link_col_type = pe.get_dest_type(); + } + + realm_precondition(suffix_path.empty(), + util::format("An extraneous property '%1' was found for operation '%2'", + suffix_path, util::collection_operator_to_str(OpType))); + } + } + template + auto value_of_type_for_query() const + { + return CollectionOperatorGetter::convert(*this); + } +}; + + +// Certain operations are disabled for some types (eg. a sum of timestamps is invalid). +// The operations that are supported have a specialisation with std::enable_if for that type below +// any type/operation combination that is not specialised will get the runtime error from the following +// default implementation. The return type is just a dummy to make things compile. +template +struct CollectionOperatorGetter { + static Columns convert(const CollectionOperatorExpression& op) { + throw std::runtime_error(util::format("Predicate error: comparison of type '%1' with result of '%2' is not supported.", + type_to_str(), + collection_operator_to_str(op.operation_type))); + } +}; + +template +struct CollectionOperatorGetter::value> >{ + static SubColumnAggregate > convert(const CollectionOperatorExpression& expr) + { + if (expr.pe.dest_type_is_backlink()) { + return expr.table_getter()->template column(*expr.pe.get_dest_table(), expr.pe.get_dest_ndx()).template column(expr.post_link_col_ndx).min(); + } else { + return expr.table_getter()->template column(expr.pe.get_dest_ndx()).template column(expr.post_link_col_ndx).min(); + } + } +}; + +template +struct CollectionOperatorGetter::value> >{ + static SubColumnAggregate > convert(const CollectionOperatorExpression& expr) + { + if (expr.pe.dest_type_is_backlink()) { + return expr.table_getter()->template column(*expr.pe.get_dest_table(), expr.pe.get_dest_ndx()).template column(expr.post_link_col_ndx).max(); + } else { + return expr.table_getter()->template column(expr.pe.get_dest_ndx()).template column(expr.post_link_col_ndx).max(); + } + } +}; + +template +struct CollectionOperatorGetter::value> >{ + static SubColumnAggregate > convert(const CollectionOperatorExpression& expr) + { + if (expr.pe.dest_type_is_backlink()) { + return expr.table_getter()->template column(*expr.pe.get_dest_table(), expr.pe.get_dest_ndx()).template column(expr.post_link_col_ndx).sum(); + } else { + return expr.table_getter()->template column(expr.pe.get_dest_ndx()).template column(expr.post_link_col_ndx).sum(); + } + } +}; + +template +struct CollectionOperatorGetter::value> >{ + static SubColumnAggregate > convert(const CollectionOperatorExpression& expr) + { + if (expr.pe.dest_type_is_backlink()) { + return expr.table_getter()->template column(*expr.pe.get_dest_table(), expr.pe.get_dest_ndx()).template column(expr.post_link_col_ndx).average(); + } else { + return expr.table_getter()->template column(expr.pe.get_dest_ndx()).template column(expr.post_link_col_ndx).average(); + } + } +}; + +template +struct CollectionOperatorGetter::value> >{ + static LinkCount convert(const CollectionOperatorExpression& expr) + { + if (expr.pe.dest_type_is_backlink()) { + return expr.table_getter()->template column(*expr.pe.get_dest_table(), expr.pe.get_dest_ndx()).count(); + } else { + return expr.table_getter()->template column(expr.pe.get_dest_ndx()).count(); + } + } +}; + + +template +struct CollectionOperatorGetter::value> >{ + static BacklinkCount convert(const CollectionOperatorExpression& expr) + { + if (expr.pe.link_chain.empty() || expr.pe.get_dest_ndx() == realm::npos) { + // here we are operating on the current table from a "@links.@count" query with no link keypath prefix + return expr.table_getter()->template get_backlink_count(); + } else { + if (expr.pe.dest_type_is_backlink()) { + return expr.table_getter()->template column(*expr.pe.get_dest_table(), expr.pe.get_dest_ndx()).template backlink_count(); + } else { + return expr.table_getter()->template column(expr.pe.get_dest_ndx()).template backlink_count(); + } + } + } +}; + + +template <> +struct CollectionOperatorGetter{ + static SizeOperator > convert(const CollectionOperatorExpression& expr) + { + return expr.table_getter()->template column(expr.pe.get_dest_ndx()).size(); + } +}; + +template <> +struct CollectionOperatorGetter{ + static SizeOperator > convert(const CollectionOperatorExpression& expr) + { + return expr.table_getter()->template column(expr.pe.get_dest_ndx()).size(); + } +}; + +} // namespace parser +} // namespace realm + +#endif // REALM_COLLECTION_OPERATOR_EXPRESSION_HPP diff --git a/Pods/Realm/include/core/realm/parser/expression_container.hpp b/Pods/Realm/include/core/realm/parser/expression_container.hpp new file mode 100644 index 0000000..7e596b9 --- /dev/null +++ b/Pods/Realm/include/core/realm/parser/expression_container.hpp @@ -0,0 +1,79 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#ifndef REALM_EXPRESSION_CONTAINER_HPP +#define REALM_EXPRESSION_CONTAINER_HPP + +#include + +#include "collection_operator_expression.hpp" +#include "parser.hpp" +#include "property_expression.hpp" +#include "query_builder.hpp" +#include "subquery_expression.hpp" +#include "value_expression.hpp" + +namespace realm { +namespace parser { + +class ExpressionContainer +{ +public: + ExpressionContainer(Query& query, const parser::Expression& e, query_builder::Arguments& args, parser::KeyPathMapping& mapping); + + bool is_null(); + + PropertyExpression& get_property(); + ValueExpression& get_value(); + CollectionOperatorExpression& get_min(); + CollectionOperatorExpression& get_max(); + CollectionOperatorExpression& get_sum(); + CollectionOperatorExpression& get_avg(); + CollectionOperatorExpression& get_count(); + CollectionOperatorExpression& get_backlink_count(); + CollectionOperatorExpression& get_size_string(); + CollectionOperatorExpression& get_size_binary(); + SubqueryExpression& get_subexpression(); + + DataType check_type_compatibility(DataType type); + DataType get_comparison_type(ExpressionContainer& rhs); + + enum class ExpressionInternal + { + exp_Value, + exp_Property, + exp_OpMin, + exp_OpMax, + exp_OpSum, + exp_OpAvg, + exp_OpCount, + exp_OpSizeString, + exp_OpSizeBinary, + exp_OpBacklinkCount, + exp_SubQuery + }; + + ExpressionInternal type; +private: + util::Any storage; +}; + +} // namespace parser +} // namespace realm + +#endif // REALM_EXPRESSION_CONTAINER_HPP diff --git a/Pods/Realm/include/core/realm/parser/keypath_mapping.hpp b/Pods/Realm/include/core/realm/parser/keypath_mapping.hpp new file mode 100644 index 0000000..ef71d8d --- /dev/null +++ b/Pods/Realm/include/core/realm/parser/keypath_mapping.hpp @@ -0,0 +1,75 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#ifndef REALM_KEYPATH_MAPPING_HPP +#define REALM_KEYPATH_MAPPING_HPP + +#include + +#include "parser_utils.hpp" + +#include +#include + +namespace realm { +namespace parser { + +struct KeyPathElement +{ + ConstTableRef table; + size_t col_ndx; + DataType col_type; + bool is_backlink; +}; + +class BacklinksRestrictedError : public std::runtime_error { +public: + BacklinksRestrictedError(const std::string& msg) : std::runtime_error(msg) {} + /// runtime_error::what() returns the msg provided in the constructor. +}; + +struct TableAndColHash { + std::size_t operator () (const std::pair &p) const; +}; + + +// This class holds state which allows aliasing variable names in key paths used in queries. +// It is used to allow variable naming in subqueries such as 'SUBQUERY(list, $obj, $obj.intCol = 5).@count' +// It can also be used to allow querying named backlinks if bindings provide the mappings themselves. +class KeyPathMapping +{ +public: + KeyPathMapping(); + // returns true if added, false if duplicate key already exists + bool add_mapping(ConstTableRef table, std::string name, std::string alias); + void remove_mapping(ConstTableRef table, std::string name); + bool has_mapping(ConstTableRef table, std::string name); + KeyPathElement process_next_path(ConstTableRef table, KeyPath& path, size_t& index); + void set_allow_backlinks(bool allow); + void set_backlink_class_prefix(std::string prefix); + static Table* table_getter(TableRef table, const std::vector& links); +protected: + bool m_allow_backlinks; + std::string m_backlink_class_prefix; + std::unordered_map, std::string, TableAndColHash> m_mapping; +}; + +} // namespace parser +} // namespace realm + +#endif // REALM_KEYPATH_MAPPING_HPP diff --git a/Pods/Realm/include/core/realm/parser/parser.hpp b/Pods/Realm/include/core/realm/parser/parser.hpp new file mode 100644 index 0000000..ef3c64a --- /dev/null +++ b/Pods/Realm/include/core/realm/parser/parser.hpp @@ -0,0 +1,140 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#ifndef REALM_PARSER_HPP +#define REALM_PARSER_HPP + +#include +#include +#include + +namespace realm { + +namespace parser { + +struct Predicate; + +struct Expression +{ + enum class Type { None, Number, String, KeyPath, Argument, True, False, Null, Timestamp, Base64, SubQuery } type; + enum class KeyPathOp { None, Min, Max, Avg, Sum, Count, SizeString, SizeBinary, BacklinkCount } collection_op; + std::string s; + std::vector time_inputs; + std::string op_suffix; + std::string subquery_path, subquery_var; + std::shared_ptr subquery; + Expression(Type t = Type::None, std::string input = "") : type(t), collection_op(KeyPathOp::None), s(input) {} + Expression(std::vector&& timestamp) : type(Type::Timestamp), collection_op(KeyPathOp::None), time_inputs(timestamp) {} + Expression(std::string prefix, KeyPathOp op, std::string suffix) : type(Type::KeyPath), collection_op(op), s(prefix), op_suffix(suffix) {} +}; + +struct Predicate +{ + enum class Type + { + Comparison, + Or, + And, + True, + False + } type = Type::And; + + enum class Operator + { + None, + Equal, + NotEqual, + LessThan, + LessThanOrEqual, + GreaterThan, + GreaterThanOrEqual, + BeginsWith, + EndsWith, + Contains, + Like, + In + }; + + enum class OperatorOption + { + None, + CaseInsensitive, + }; + + enum class ComparisonType + { + Unspecified, + Any, + All, + None, + }; + + struct Comparison + { + Operator op = Operator::None; + OperatorOption option = OperatorOption::None; + Expression expr[2] = {{Expression::Type::None, ""}, {Expression::Type::None, ""}}; + ComparisonType compare_type = ComparisonType::Unspecified; + }; + + struct Compound + { + std::vector sub_predicates; + }; + + Comparison cmpr; + Compound cpnd; + + bool negate = false; + + Predicate(Type t, bool n = false) : type(t), negate(n) {} +}; + +struct DescriptorOrderingState +{ + struct PropertyState + { + std::string key_path; + bool ascending; + }; + struct SingleOrderingState + { + std::vector properties; + bool is_distinct; + }; + std::vector orderings; +}; + +struct ParserResult +{ + ParserResult(Predicate p, DescriptorOrderingState o) + : predicate(p) + , ordering(o) {} + Predicate predicate; + DescriptorOrderingState ordering; +}; + +ParserResult parse(const std::string &query); + +// run the analysis tool to check for cycles in the grammar +// returns the number of problems found and prints some info to std::cout +size_t analyze_grammar(); +} +} + +#endif // REALM_PARSER_HPP diff --git a/Pods/Realm/include/core/realm/parser/parser_utils.hpp b/Pods/Realm/include/core/realm/parser/parser_utils.hpp new file mode 100644 index 0000000..12b79c9 --- /dev/null +++ b/Pods/Realm/include/core/realm/parser/parser_utils.hpp @@ -0,0 +1,87 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#ifndef REALM_PARSER_UTILS_HPP +#define REALM_PARSER_UTILS_HPP + +#include +#include +#include "parser.hpp" + +#include +#include + +namespace realm { + +class Table; +class Timestamp; +struct Link; + +namespace util { + +// check a precondition and throw an exception if it is not met +// this should be used iff the condition being false indicates a bug in the caller +// of the function checking its preconditions +#define realm_precondition(condition, message) if (!REALM_LIKELY(condition)) { throw std::logic_error(message); } + + +template +const char* type_to_str(); + +template <> +const char* type_to_str(); +template <> +const char* type_to_str(); +template <> +const char* type_to_str(); +template <> +const char* type_to_str(); +template <> +const char* type_to_str(); +template <> +const char* type_to_str(); +template <> +const char* type_to_str(); +template <> +const char* type_to_str(); + +const char* data_type_to_str(DataType type); +const char* collection_operator_to_str(parser::Expression::KeyPathOp op); +const char* comparison_type_to_str(parser::Predicate::ComparisonType type); + +using KeyPath = std::vector; +KeyPath key_path_from_string(const std::string &s); +std::string key_path_to_string(const KeyPath& keypath); +StringData get_printable_table_name(StringData name); +StringData get_printable_table_name(const Table& table); + +template +T stot(std::string const& s) { + std::istringstream iss(s); + T value; + iss >> value; + if (iss.fail()) { + throw std::invalid_argument(util::format("Cannot convert string '%1'", s)); + } + return value; +} + +} // namespace utils +} // namespace realm + +#endif // REALM_PARSER_UTILS_HPP diff --git a/Pods/Realm/include/core/realm/parser/property_expression.hpp b/Pods/Realm/include/core/realm/parser/property_expression.hpp new file mode 100644 index 0000000..8f4a7ec --- /dev/null +++ b/Pods/Realm/include/core/realm/parser/property_expression.hpp @@ -0,0 +1,78 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#ifndef REALM_PROPERTY_EXPRESSION_HPP +#define REALM_PROPERTY_EXPRESSION_HPP + +#include +#include +#include + +namespace realm { +namespace parser { + +struct PropertyExpression +{ + Query &query; + std::vector link_chain; + DataType get_dest_type() const; + size_t get_dest_ndx() const; + ConstTableRef get_dest_table() const; + bool dest_type_is_backlink() const; + + PropertyExpression(Query &q, const std::string &key_path_string, parser::KeyPathMapping& mapping); + + Table* table_getter() const; + + template + auto value_of_type_for_query() const + { + return this->table_getter()->template column(get_dest_ndx()); + } +}; + +inline DataType PropertyExpression::get_dest_type() const +{ + REALM_ASSERT_DEBUG(link_chain.size() > 0); + return link_chain.back().col_type; +} + +inline bool PropertyExpression::dest_type_is_backlink() const +{ + REALM_ASSERT_DEBUG(link_chain.size() > 0); + return link_chain.back().is_backlink; +} + +inline size_t PropertyExpression::get_dest_ndx() const +{ + REALM_ASSERT_DEBUG(link_chain.size() > 0); + return link_chain.back().col_ndx; +} + +inline ConstTableRef PropertyExpression::get_dest_table() const +{ + REALM_ASSERT_DEBUG(link_chain.size() > 0); + return link_chain.back().table; +} + + +} // namespace parser +} // namespace realm + +#endif // REALM_PROPERTY_EXPRESSION_HPP + diff --git a/Pods/Realm/include/core/realm/parser/query_builder.hpp b/Pods/Realm/include/core/realm/parser/query_builder.hpp new file mode 100644 index 0000000..ef91bb1 --- /dev/null +++ b/Pods/Realm/include/core/realm/parser/query_builder.hpp @@ -0,0 +1,155 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#ifndef REALM_QUERY_BUILDER_HPP +#define REALM_QUERY_BUILDER_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace realm { +class Query; +class Realm; +class Table; +template class BasicRowExpr; +using RowExpr = BasicRowExpr
; + +namespace parser { + struct Predicate; + struct DescriptorOrderingState; +} + +namespace query_builder { +class Arguments; + +void apply_predicate(Query& query, const parser::Predicate& predicate, Arguments& arguments, parser::KeyPathMapping = parser::KeyPathMapping()); + +void apply_ordering(DescriptorOrdering& ordering, ConstTableRef target, const parser::DescriptorOrderingState& state, Arguments& arguments); +void apply_ordering(DescriptorOrdering& ordering, ConstTableRef target, const parser::DescriptorOrderingState& state); + + +struct AnyContext +{ + template + T unbox(const util::Any& wrapper) { + return util::any_cast(wrapper); + } + bool is_null(const util::Any& wrapper) { + if (!wrapper.has_value()) { + return true; + } + if (wrapper.type() == typeid(realm::null)) { + return true; + } + return false; + } +}; + +class Arguments { +public: + virtual bool bool_for_argument(size_t argument_index) = 0; + virtual long long long_for_argument(size_t argument_index) = 0; + virtual float float_for_argument(size_t argument_index) = 0; + virtual double double_for_argument(size_t argument_index) = 0; + virtual StringData string_for_argument(size_t argument_index) = 0; + virtual BinaryData binary_for_argument(size_t argument_index) = 0; + virtual Timestamp timestamp_for_argument(size_t argument_index) = 0; + virtual size_t object_index_for_argument(size_t argument_index) = 0; + virtual bool is_argument_null(size_t argument_index) = 0; + // dynamic conversion space with lifetime tied to this + // it is used for storing literal binary/string data + std::vector buffer_space; +}; + +template +class ArgumentConverter : public Arguments { +public: + ArgumentConverter(ContextType& context, const ValueType* arguments, size_t count) + : m_ctx(context) + , m_arguments(arguments) + , m_count(count) + {} + + bool bool_for_argument(size_t i) override { return get(i); } + long long long_for_argument(size_t i) override { return get(i); } + float float_for_argument(size_t i) override { return get(i); } + double double_for_argument(size_t i) override { return get(i); } + StringData string_for_argument(size_t i) override { return get(i); } + BinaryData binary_for_argument(size_t i) override { return get(i); } + Timestamp timestamp_for_argument(size_t i) override { return get(i); } + size_t object_index_for_argument(size_t i) override { return get(i).get_index(); } + bool is_argument_null(size_t i) override { return m_ctx.is_null(at(i)); } + +private: + ContextType& m_ctx; + const ValueType* m_arguments; + size_t m_count; + + const ValueType& at(size_t index) const + { + if (index >= m_count) { + std::string error_message; + if (m_count) { + error_message = util::format("Request for argument at index %1 but only %2 argument%3 provided", index, m_count, m_count == 1 ? " is" : "s are"); + } else { + error_message = util::format("Request for argument at index %1 but no arguments are provided", index); + } + throw std::out_of_range(error_message); + } + return m_arguments[index]; + } + + template + T get(size_t index) const + { + return m_ctx.template unbox(at(index)); + } +}; + +class NoArgsError : public std::runtime_error { +public: + NoArgsError() : std::runtime_error("Attempt to retreive an argument when no arguments were given") {} +}; + +class NoArguments : public Arguments { +public: + bool bool_for_argument(size_t) { throw NoArgsError(); } + long long long_for_argument(size_t) { throw NoArgsError(); } + float float_for_argument(size_t) { throw NoArgsError(); } + double double_for_argument(size_t) { throw NoArgsError(); } + StringData string_for_argument(size_t) { throw NoArgsError(); } + BinaryData binary_for_argument(size_t) { throw NoArgsError(); } + Timestamp timestamp_for_argument(size_t) { throw NoArgsError(); } + size_t object_index_for_argument(size_t) { throw NoArgsError(); } + bool is_argument_null(size_t) { throw NoArgsError(); } +}; + +} // namespace query_builder +} // namespace realm + +#endif // REALM_QUERY_BUILDER_HPP diff --git a/Pods/Realm/include/core/realm/parser/subquery_expression.hpp b/Pods/Realm/include/core/realm/parser/subquery_expression.hpp new file mode 100644 index 0000000..20c04d1 --- /dev/null +++ b/Pods/Realm/include/core/realm/parser/subquery_expression.hpp @@ -0,0 +1,112 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#ifndef REALM_SUBQUERY_EXPRESSION_HPP +#define REALM_SUBQUERY_EXPRESSION_HPP + +#include +#include +#include +#include + +#include "parser_utils.hpp" + +namespace realm { +namespace parser { + +template +struct SubqueryGetter; + +struct SubqueryExpression +{ + std::string var_name; + Query &query; + Query subquery; + std::vector link_chain; + DataType get_dest_type() const; + size_t get_dest_ndx() const; + ConstTableRef get_dest_table() const; + bool dest_type_is_backlink() const; + + + SubqueryExpression(Query &q, const std::string &key_path_string, const std::string &variable_name, parser::KeyPathMapping &mapping); + Query& get_subquery(); + + Table* table_getter() const; + + template + auto value_of_type_for_query() const + { + return SubqueryGetter::convert(*this); + } +}; + +inline DataType SubqueryExpression::get_dest_type() const +{ + REALM_ASSERT_DEBUG(link_chain.size() > 0); + return link_chain.back().col_type; +} + +inline bool SubqueryExpression::dest_type_is_backlink() const +{ + REALM_ASSERT_DEBUG(link_chain.size() > 0); + return link_chain.back().is_backlink; +} + +inline size_t SubqueryExpression::get_dest_ndx() const +{ + REALM_ASSERT_DEBUG(link_chain.size() > 0); + return link_chain.back().col_ndx; +} + +inline ConstTableRef SubqueryExpression::get_dest_table() const +{ + REALM_ASSERT_DEBUG(link_chain.size() > 0); + return link_chain.back().table; +} + +// Certain operations are disabled for some types (eg. a sum of timestamps is invalid). +// The operations that are supported have a specialisation with std::enable_if for that type below +// any type/operation combination that is not specialised will get the runtime error from the following +// default implementation. The return type is just a dummy to make things compile. +template +struct SubqueryGetter { + static Columns convert(const SubqueryExpression&) { + throw std::runtime_error(util::format("Predicate error: comparison of type '%1' with result of a subquery count is not supported.", + type_to_str())); + } +}; + +template +struct SubqueryGetter::value> >{ + static SubQueryCount convert(const SubqueryExpression& expr) + { + if (expr.dest_type_is_backlink()) { + return expr.table_getter()->template column(*expr.get_dest_table(), expr.get_dest_ndx(), expr.subquery).count(); + } else { + return expr.table_getter()->template column(expr.get_dest_ndx(), expr.subquery).count(); + } + } +}; + +} // namespace parser +} // namespace realm + +#endif // REALM_SUBQUERY_EXPRESSION_HPP + diff --git a/Pods/Realm/include/util/compiler.hpp b/Pods/Realm/include/core/realm/parser/value_expression.hpp similarity index 52% rename from Pods/Realm/include/util/compiler.hpp rename to Pods/Realm/include/core/realm/parser/value_expression.hpp index 0ccc848..09ac257 100644 --- a/Pods/Realm/include/util/compiler.hpp +++ b/Pods/Realm/include/core/realm/parser/value_expression.hpp @@ -16,27 +16,28 @@ // //////////////////////////////////////////////////////////////////////////// -#ifndef REALM_UTIL_COMPILER_HPP -#define REALM_UTIL_COMPILER_HPP +#ifndef REALM_VALUE_EXPRESSION_HPP +#define REALM_VALUE_EXPRESSION_HPP -#ifdef __has_cpp_attribute -#define REALM_HAS_CPP_ATTRIBUTE(attr) __has_cpp_attribute(attr) -#else -#define REALM_HAS_CPP_ATTRIBUTE(attr) 0 -#endif +#include "parser.hpp" +#include "query_builder.hpp" -#if REALM_HAS_CPP_ATTRIBUTE(clang::fallthrough) -#define REALM_FALLTHROUGH [[clang::fallthrough]] -#else -#define REALM_FALLTHROUGH -#endif +namespace realm { +namespace parser { -// This should be renamed to REALM_UNREACHABLE as soon as REALM_UNREACHABLE is renamed to -// REALM_ASSERT_NOT_REACHED which will better reflect its nature -#if __GNUC__ || __clang__ -#define REALM_COMPILER_HINT_UNREACHABLE __builtin_unreachable -#else -#define REALM_COMPILER_HINT_UNREACHABLE abort -#endif +struct ValueExpression +{ + const parser::Expression* value; + query_builder::Arguments* arguments; + std::function
table_getter; -#endif // REALM_UTIL_COMPILER_HPP + ValueExpression(Query& query, query_builder::Arguments* args, const parser::Expression* v); + bool is_null(); + template + RetType value_of_type_for_query(); +}; + +} // namespace parser +} // namespace realm + +#endif // REALM_VALUE_EXPRESSION_HPP diff --git a/Pods/Realm/include/core/realm/query.hpp b/Pods/Realm/include/core/realm/query.hpp index f166cd9..9b1ad07 100644 --- a/Pods/Realm/include/core/realm/query.hpp +++ b/Pods/Realm/include/core/realm/query.hpp @@ -42,6 +42,7 @@ #include #include #include +#include namespace realm { @@ -57,6 +58,10 @@ class Expression; class SequentialGetterBase; class Group; +namespace metrics { +class QueryInfo; +} + struct QueryGroup { enum class State { Default, @@ -171,6 +176,15 @@ class Query final { Query& less_equal(size_t column_ndx, Timestamp value); Query& less(size_t column_ndx, Timestamp value); + // Conditions: size + Query& size_equal(size_t column_ndx, int64_t value); + Query& size_not_equal(size_t column_ndx, int64_t value); + Query& size_greater(size_t column_ndx, int64_t value); + Query& size_greater_equal(size_t column_ndx, int64_t value); + Query& size_less_equal(size_t column_ndx, int64_t value); + Query& size_less(size_t column_ndx, int64_t value); + Query& size_between(size_t column_ndx, int64_t from, int64_t to); + // Conditions: bool Query& equal(size_t column_ndx, bool value); @@ -219,11 +233,12 @@ class Query final { Query& not_equal(size_t column_ndx, const char* c_str, bool case_sensitive = true); // Conditions: binary data - Query& equal(size_t column_ndx, BinaryData value); - Query& not_equal(size_t column_ndx, BinaryData value); - Query& begins_with(size_t column_ndx, BinaryData value); - Query& ends_with(size_t column_ndx, BinaryData value); - Query& contains(size_t column_ndx, BinaryData value); + Query& equal(size_t column_ndx, BinaryData value, bool case_sensitive = true); + Query& not_equal(size_t column_ndx, BinaryData value, bool case_sensitive = true); + Query& begins_with(size_t column_ndx, BinaryData value, bool case_sensitive = true); + Query& ends_with(size_t column_ndx, BinaryData value, bool case_sensitive = true); + Query& contains(size_t column_ndx, BinaryData value, bool case_sensitive = true); + Query& like(size_t column_ndx, BinaryData b, bool case_sensitive = true); // Negation Query& Not(); @@ -328,6 +343,9 @@ class Query final { std::string validate(); + std::string get_description() const; + std::string get_description(util::serializer::SerialisationState& state) const; + private: Query(Table& table, TableViewBase* tv = nullptr); void create(); @@ -338,8 +356,6 @@ class Query final { void handle_pending_not(); void set_table(TableRef tr); - static bool comp(const std::pair& a, const std::pair& b); - public: using HandoverPatch = QueryHandoverPatch; @@ -391,6 +407,9 @@ class Query final { template Query& add_condition(size_t column_ndx, T value); + template + Query& add_size_condition(size_t column_ndx, int64_t value); + template double average(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, size_t end = size_t(-1), size_t limit = size_t(-1)) const; @@ -419,6 +438,7 @@ class Query final { friend class Table; friend class TableViewBase; + friend class metrics::QueryInfo; std::string error_code; diff --git a/Pods/Realm/include/core/realm/query_conditions.hpp b/Pods/Realm/include/core/realm/query_conditions.hpp index d7d571c..f9d66fb 100644 --- a/Pods/Realm/include/core/realm/query_conditions.hpp +++ b/Pods/Realm/include/core/realm/query_conditions.hpp @@ -86,6 +86,11 @@ struct Contains : public HackClass { return false; } + static std::string description() + { + return "CONTAINS"; + } + static const int condition = -1; }; @@ -95,14 +100,21 @@ struct Like : public HackClass { { return v2.like(v1); } + bool operator()(BinaryData b1, const char*, const char*, BinaryData b2, bool = false, bool = false) const + { + StringData s1(b1.data(), b1.size()); + StringData s2(b2.data(), b2.size()); + return s2.like(s1); + } bool operator()(StringData v1, StringData v2, bool = false, bool = false) const { return v2.like(v1); } - bool operator()(BinaryData, BinaryData, bool = false, bool = false) const + bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const { - REALM_ASSERT(false); - return false; + StringData s1(b1.data(), b1.size()); + StringData s2(b2.data(), b2.size()); + return s2.like(s1); } template @@ -125,6 +137,11 @@ struct Like : public HackClass { return false; } + static std::string description() + { + return "LIKE"; + } + static const int condition = -1; }; @@ -156,6 +173,11 @@ struct BeginsWith : public HackClass { return false; } + static std::string description() + { + return "BEGINSWITH"; + } + static const int condition = -1; }; @@ -187,6 +209,11 @@ struct EndsWith : public HackClass { return false; } + static std::string description() + { + return "ENDSWITH"; + } + static const int condition = -1; }; @@ -217,6 +244,11 @@ struct Equal { { return (v == 0 && ubound == 0 && lbound == 0); } + + static std::string description() + { + return "=="; + } }; struct NotEqual { @@ -255,6 +287,11 @@ struct NotEqual { REALM_ASSERT(false); return false; } + + static std::string description() + { + return "!="; + } }; // Does v2 contain v1? @@ -284,6 +321,12 @@ struct ContainsIns : public HackClass { std::string v1_lower = case_map(v1, false, IgnoreErrors); return search_case_fold(v2, v1_upper.c_str(), v1_lower.c_str(), v1.size()) != v2.size(); } + bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const + { + StringData s1(b1.data(), b1.size()); + StringData s2(b2.data(), b2.size()); + return this->operator()(s1, s2, false, false); + } // Case insensitive Boyer-Moore version bool operator()(StringData v1, const char* v1_upper, const char* v1_lower, const std::array &charmap, StringData v2) const @@ -316,6 +359,11 @@ struct ContainsIns : public HackClass { return false; } + static std::string description() + { + return "CONTAINS[c]"; + } + static const int condition = -1; }; @@ -330,6 +378,16 @@ struct LikeIns : public HackClass { return string_like_ins(v2, v1_lower, v1_upper); } + bool operator()(BinaryData b1, const char* b1_upper, const char* b1_lower, BinaryData b2, bool = false, + bool = false) const + { + if (b2.is_null() || b1.is_null()) { + return (b2.is_null() && b1.is_null()); + } + StringData s2(b2.data(), b2.size()); + + return string_like_ins(s2, b1_lower, b1_upper); + } // Slow version, used if caller hasn't stored an upper and lower case version bool operator()(StringData v1, StringData v2, bool = false, bool = false) const @@ -342,6 +400,18 @@ struct LikeIns : public HackClass { std::string v1_lower = case_map(v1, false, IgnoreErrors); return string_like_ins(v2, v1_lower, v1_upper); } + bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const + { + if (b2.is_null() || b1.is_null()) { + return (b2.is_null() && b1.is_null()); + } + StringData s1(b1.data(), b1.size()); + StringData s2(b2.data(), b2.size()); + + std::string s1_upper = case_map(s1, true, IgnoreErrors); + std::string s1_lower = case_map(s1, false, IgnoreErrors); + return string_like_ins(s2, s1_lower, s1_upper); + } template bool operator()(A, B) const @@ -361,6 +431,11 @@ struct LikeIns : public HackClass { return false; } + static std::string description() + { + return "LIKE[c]"; + } + static const int condition = -1; }; @@ -386,6 +461,12 @@ struct BeginsWithIns : public HackClass { std::string v1_lower = case_map(v1, false, IgnoreErrors); return equal_case_fold(v2.prefix(v1.size()), v1_upper.c_str(), v1_lower.c_str()); } + bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const + { + StringData s1(b1.data(), b1.size()); + StringData s2(b2.data(), b2.size()); + return this->operator()(s1, s2, false, false); + } template bool operator()(A, B) const @@ -405,6 +486,11 @@ struct BeginsWithIns : public HackClass { return false; } + static std::string description() + { + return "BEGINSWITH[c]"; + } + static const int condition = -1; }; @@ -431,6 +517,12 @@ struct EndsWithIns : public HackClass { std::string v1_lower = case_map(v1, false, IgnoreErrors); return equal_case_fold(v2.suffix(v1.size()), v1_upper.c_str(), v1_lower.c_str()); } + bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const + { + StringData s1(b1.data(), b1.size()); + StringData s2(b2.data(), b2.size()); + return this->operator()(s1, s2, false, false); + } template bool operator()(A, B) const @@ -450,6 +542,11 @@ struct EndsWithIns : public HackClass { return false; } + static std::string description() + { + return "ENDSWITH[c]"; + } + static const int condition = -1; }; @@ -475,6 +572,12 @@ struct EqualIns : public HackClass { std::string v1_lower = case_map(v1, false, IgnoreErrors); return equal_case_fold(v2, v1_upper.c_str(), v1_lower.c_str()); } + bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const + { + StringData s1(b1.data(), b1.size()); + StringData s2(b2.data(), b2.size()); + return this->operator()(s1, s2, false, false); + } template bool operator()(A, B) const @@ -494,6 +597,11 @@ struct EqualIns : public HackClass { return false; } + static std::string description() + { + return "==[c]"; + } + static const int condition = -1; }; @@ -518,6 +626,12 @@ struct NotEqualIns : public HackClass { std::string v1_lower = case_map(v1, false, IgnoreErrors); return !equal_case_fold(v2, v1_upper.c_str(), v1_lower.c_str()); } + bool operator()(BinaryData b1, BinaryData b2, bool = false, bool = false) const + { + StringData s1(b1.data(), b1.size()); + StringData s2(b2.data(), b2.size()); + return this->operator()(s1, s2, false, false); + } template bool operator()(A, B) const @@ -532,6 +646,11 @@ struct NotEqualIns : public HackClass { return false; } + static std::string description() + { + return "!=[c]"; + } + static const int condition = -1; }; @@ -563,6 +682,11 @@ struct Greater { static_cast(ubound); return lbound > v; } + + static std::string description() + { + return ">"; + } }; struct None { @@ -592,6 +716,11 @@ struct None { static_cast(v); return true; } + + static std::string description() + { + return "none"; + } }; struct NotNull { @@ -621,6 +750,10 @@ struct NotNull { static_cast(v); return true; } + static std::string description() + { + return "!= NULL"; + } }; @@ -651,6 +784,10 @@ struct Less { static_cast(lbound); return ubound < v; } + static std::string description() + { + return "<"; + } }; struct LessEqual : public HackClass { @@ -669,6 +806,10 @@ struct LessEqual : public HackClass { REALM_ASSERT(false); return false; } + static std::string description() + { + return "<="; + } static const int condition = -1; }; @@ -688,6 +829,10 @@ struct GreaterEqual : public HackClass { REALM_ASSERT(false); return false; } + static std::string description() + { + return ">="; + } static const int condition = -1; }; diff --git a/Pods/Realm/include/core/realm/query_engine.hpp b/Pods/Realm/include/core/realm/query_engine.hpp index f916e80..3bf2949 100644 --- a/Pods/Realm/include/core/realm/query_engine.hpp +++ b/Pods/Realm/include/core/realm/query_engine.hpp @@ -86,13 +86,10 @@ AggregateState State of the aggregate - contains a state variable that stor #include #include +#include #include #include -#include -#include -#include -#include #include #include #include @@ -106,16 +103,21 @@ AggregateState State of the aggregate - contains a state variable that stor #include #include #include +#include #include +#include #include -#include +#include #include -#include #include +#include +#include +#include +#include #include -#if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219 +#if REALM_X86_OR_X64_TRUE && defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219 #include #endif @@ -138,7 +140,6 @@ const size_t bitwidth_time_unit = 64; typedef bool (*CallbackDummy)(int64_t); - class ParentNode { typedef ParentNode ThisType; @@ -170,8 +171,12 @@ class ParentNode { virtual void init() { + // Verify that the cached column accessor is still valid + verify_column(); // throws + if (m_child) m_child->init(); + m_column_action_specializer = nullptr; } @@ -261,6 +266,28 @@ class ParentNode { m_child->apply_handover_patch(patches, group); } + virtual void verify_column() const = 0; + + virtual std::string describe(util::serializer::SerialisationState&) const + { + return ""; + } + + virtual std::string describe_condition() const + { + return "matches"; + } + + virtual std::string describe_expression(util::serializer::SerialisationState& state) const + { + std::string s; + s = describe(state); + if (m_child) { + s = s + " and " + m_child->describe_expression(state); + } + return s; + } + std::unique_ptr m_child; std::vector m_children; size_t m_condition_column_idx = npos; // Column of search criteria @@ -308,13 +335,21 @@ class ParentNode { } } + void do_verify_column(const ColumnBase* col, size_t col_ndx = npos) const + { + if (col_ndx == npos) + col_ndx = m_condition_column_idx; + if (m_table && col_ndx != npos) { + m_table->verify_column(col_ndx, col); + } + } + private: virtual void table_changed() = 0; }; // For conditions on a subtable (encapsulated in subtable()...end_subtable()). These return the parent row as match if -// and -// only if one or more subtable rows match the condition. +// and only if one or more subtable rows match the condition. class SubtableNode : public ParentNode { public: SubtableNode(size_t column, std::unique_ptr condition) @@ -326,6 +361,8 @@ class SubtableNode : public ParentNode { void init() override { + ParentNode::init(); + m_dD = 10.0; // m_condition is first node in condition of subtable query. @@ -335,10 +372,6 @@ class SubtableNode : public ParentNode { std::vector v; m_condition->gather_children(v); } - - // m_child is next node of parent query - if (m_child) - m_child->init(); } void table_changed() override @@ -351,6 +384,12 @@ class SubtableNode : public ParentNode { m_column = &m_table->get_column_mixed(m_condition_column_idx); } + void verify_column() const override + { + if (m_table) + m_table->verify_column(m_condition_column_idx, m_column); + } + std::string validate() override { if (error_code != "") @@ -361,17 +400,23 @@ class SubtableNode : public ParentNode { return m_condition->validate(); } + std::string describe(util::serializer::SerialisationState&) const override + { + throw SerialisationError("Serialising a query which contains a subtable expression is currently unsupported."); + } + + size_t find_first_local(size_t start, size_t end) override { REALM_ASSERT(m_table); REALM_ASSERT(m_condition); for (size_t s = start; s < end; ++s) { - const Table* subtable; + ConstTableRef subtable; // TBD: optimize this back to Table* if (m_col_type == col_type_Table) - subtable = static_cast(m_column)->get_subtable_ptr(s); + subtable = static_cast(m_column)->get_subtable_tableref(s); else { - subtable = static_cast(m_column)->get_subtable_ptr(s); + subtable = static_cast(m_column)->get_subtable_tableref(s); if (!subtable) continue; } @@ -615,6 +660,11 @@ class IntegerNodeBase : public ColumnNodeBase { m_condition_column = &get_column(m_condition_column_idx); } + void verify_column() const override + { + do_verify_column(m_condition_column); + } + void init() override { ColumnNodeBase::init(); @@ -626,9 +676,6 @@ class IntegerNodeBase : public ColumnNodeBase { m_leaf_end = 0; m_array_ptr.reset(); // Explicitly destroy the old one first, because we're reusing the memory. m_array_ptr.reset(new (&m_leaf_cache_storage) LeafType(m_table->get_alloc())); - - if (m_child) - m_child->init(); } void get_leaf(const ColType& col, size_t ndx) @@ -677,6 +724,7 @@ class IntegerNodeBase : public ColumnNodeBase { TFind_callback_specialized m_find_callback_specialized = nullptr; }; + // FIXME: Add specialization that uses index for TConditionFunction = Equal template class IntegerNode : public IntegerNodeBase { @@ -746,6 +794,17 @@ class IntegerNode : public IntegerNodeBase { return not_found; } + virtual std::string describe(util::serializer::SerialisationState& state) const override + { + return state.describe_column(ParentNode::m_table, IntegerNodeBase::m_condition_column->get_column_index()) + + " " + describe_condition() + " " + util::serializer::print_value(IntegerNodeBase::m_value); + } + + virtual std::string describe_condition() const override + { + return TConditionFunction::description(); + } + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override { return std::unique_ptr(new IntegerNode(*this, patches)); @@ -815,6 +874,7 @@ class IntegerNode : public IntegerNodeBase { } }; + // This node is currently used for floats and doubles only template class FloatDoubleNode : public ParentNode { @@ -840,6 +900,11 @@ class FloatDoubleNode : public ParentNode { m_condition_column.init(&get_column(m_condition_column_idx)); } + void verify_column() const override + { + do_verify_column(m_condition_column.m_column); + } + void init() override { ParentNode::init(); @@ -868,6 +933,17 @@ class FloatDoubleNode : public ParentNode { return find(false); } + virtual std::string describe(util::serializer::SerialisationState& state) const override + { + REALM_ASSERT(m_condition_column.m_column != nullptr); + return state.describe_column(ParentNode::m_table, m_condition_column.m_column->get_column_index()) + + " " + describe_condition() + " " + util::serializer::print_value(FloatDoubleNode::m_value); + } + virtual std::string describe_condition() const override + { + return TConditionFunction::description(); + } + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override { return std::unique_ptr(new FloatDoubleNode(*this, patches)); @@ -885,6 +961,66 @@ class FloatDoubleNode : public ParentNode { SequentialGetter m_condition_column; }; +template +class SizeNode : public ParentNode { +public: + SizeNode(int64_t v, size_t column) + : m_value(v) + { + m_condition_column_idx = column; + } + + void table_changed() override + { + m_condition_column = &get_column(m_condition_column_idx); + } + + void verify_column() const override + { + do_verify_column(m_condition_column); + } + + void init() override + { + ParentNode::init(); + m_dD = 10.0; + } + + size_t find_first_local(size_t start, size_t end) override + { + for (size_t s = start; s < end; ++s) { + TConditionValue v = m_condition_column->get(s); + if (v) { + int64_t sz = m_size_operator(v); + if (TConditionFunction()(sz, m_value)) + return s; + } + } + return not_found; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new SizeNode(*this, patches)); + } + + SizeNode(const SizeNode& from, QueryNodeHandoverPatches* patches) + : ParentNode(from, patches) + , m_value(from.m_value) + , m_condition_column(from.m_condition_column) + { + if (m_condition_column && patches) + m_condition_column_idx = m_condition_column->get_column_index(); + } + +private: + using TConditionValue = typename ColType::value_type; + + int64_t m_value; + const ColType* m_condition_column = nullptr; + Size m_size_operator; +}; + template class BinaryNode : public ParentNode { @@ -909,12 +1045,16 @@ class BinaryNode : public ParentNode { m_condition_column = &get_column(m_condition_column_idx); } + void verify_column() const override + { + do_verify_column(m_condition_column); + } + void init() override { - m_dD = 100.0; + ParentNode::init(); - if (m_child) - m_child->init(); + m_dD = 100.0; } size_t find_first_local(size_t start, size_t end) override @@ -928,6 +1068,14 @@ class BinaryNode : public ParentNode { return not_found; } + virtual std::string describe(util::serializer::SerialisationState& state) const override + { + REALM_ASSERT(m_condition_column != nullptr); + return state.describe_column(ParentNode::m_table, m_condition_column->get_column_index()) + + " " + TConditionFunction::description() + " " + + util::serializer::print_value(BinaryNode::m_value.get()); + } + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override { return std::unique_ptr(new BinaryNode(*this, patches)); @@ -970,12 +1118,16 @@ class TimestampNode : public ParentNode { m_condition_column = &get_column(m_condition_column_idx); } + void verify_column() const override + { + do_verify_column(m_condition_column); + } + void init() override { - m_dD = 100.0; + ParentNode::init(); - if (m_child) - m_child->init(); + m_dD = 100.0; } size_t find_first_local(size_t start, size_t end) override @@ -984,6 +1136,13 @@ class TimestampNode : public ParentNode { return ret; } + virtual std::string describe(util::serializer::SerialisationState& state) const override + { + REALM_ASSERT(m_condition_column != nullptr); + return state.describe_column(ParentNode::m_table, m_condition_column->get_column_index()) + + " " + TConditionFunction::description() + " " + util::serializer::print_value(TimestampNode::m_value); + } + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override { return std::unique_ptr(new TimestampNode(*this, patches)); @@ -1020,8 +1179,15 @@ class StringNodeBase : public ParentNode { m_column_type = get_real_column_type(m_condition_column_idx); } + void verify_column() const override + { + do_verify_column(m_condition_column); + } + void init() override { + ParentNode::init(); + m_dT = 10.0; m_probes = 0; m_matches = 0; @@ -1046,6 +1212,17 @@ class StringNodeBase : public ParentNode { m_condition_column_idx = m_condition_column->get_column_index(); } + virtual std::string describe(util::serializer::SerialisationState& state) const override + { + REALM_ASSERT(m_condition_column != nullptr); + StringData sd; + if (bool(StringNodeBase::m_value)) { + sd = StringData(StringNodeBase::m_value.value()); + } + return state.describe_column(ParentNode::m_table, m_condition_column->get_column_index()) + + " " + describe_condition() + " " + util::serializer::print_value(sd); + } + protected: util::Optional m_value; @@ -1122,9 +1299,6 @@ class StringNode : public StringNodeBase { m_dD = 100.0; StringNodeBase::init(); - - if (m_child) - m_child->init(); } @@ -1141,6 +1315,11 @@ class StringNode : public StringNodeBase { return not_found; } + virtual std::string describe_condition() const override + { + return TConditionFunction::description(); + } + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override { return std::unique_ptr(new StringNode(*this, patches)); @@ -1187,9 +1366,6 @@ class StringNode : public StringNodeBase { m_dD = 100.0; StringNodeBase::init(); - - if (m_child) - m_child->init(); } @@ -1205,7 +1381,13 @@ class StringNode : public StringNodeBase { } return not_found; } - + + virtual std::string describe_condition() const override + { + return Contains::description(); + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override { return std::unique_ptr(new StringNode(*this, patches)); @@ -1237,17 +1419,17 @@ class StringNode : public StringNodeBase { m_ucase = std::move(*upper); m_lcase = std::move(*lower); } - + if (v.size() == 0) return; - + // Build a dictionary of char-to-last distances in the search string // (zero indicates that the char is not in needle) size_t last_char_pos = m_ucase.size()-1; for (size_t i = 0; i < last_char_pos; ++i) { // we never jump longer increments than 255 chars, even if needle is longer (to fit in one byte) uint8_t jump = last_char_pos-i < 255 ? static_cast(last_char_pos-i) : 255; - + unsigned char uc = m_ucase[i]; unsigned char lc = m_lcase[i]; m_charmap[uc] = jump; @@ -1255,38 +1437,44 @@ class StringNode : public StringNodeBase { } } - + void init() override { clear_leaf_state(); - + m_dD = 100.0; - + StringNodeBase::init(); - - if (m_child) - m_child->init(); } - - + + size_t find_first_local(size_t start, size_t end) override { ContainsIns cond; - + for (size_t s = start; s < end; ++s) { StringData t = get_string(s); - + // The current behaviour is to return all results when querying for a null string. + // See comment above Query_NextGen_StringConditions on why every string including "" contains null. + if (!bool(m_value)) { + return s; + } if (cond(StringData(m_value), m_ucase.data(), m_lcase.data(), m_charmap, t)) return s; } return not_found; } - + + virtual std::string describe_condition() const override + { + return ContainsIns::description(); + } + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override { return std::unique_ptr(new StringNode(*this, patches)); } - + StringNode(const StringNode& from, QueryNodeHandoverPatches* patches) : StringNodeBase(from, patches) , m_charmap(from.m_charmap) @@ -1294,243 +1482,135 @@ class StringNode : public StringNodeBase { , m_lcase(from.m_lcase) { } - + protected: std::array m_charmap; std::string m_ucase; std::string m_lcase; }; -// Specialization for Equal condition on Strings - we specialize because we can utilize indexes (if they exist) for -// Equal. -// Future optimization: make specialization for greater, notequal, etc -template <> -class StringNode : public StringNodeBase { +class StringNodeEqualBase : public StringNodeBase { public: - StringNode(StringData v, size_t column) + StringNodeEqualBase(StringData v, size_t column) : StringNodeBase(v, column) { } - ~StringNode() noexcept override + StringNodeEqualBase(const StringNodeEqualBase& from, QueryNodeHandoverPatches* patches) + : StringNodeBase(from, patches) { - deallocate(); } - - void deallocate() noexcept + ~StringNodeEqualBase() noexcept override { - // Must be called after each query execution too free temporary resources used by the execution. Run in - // destructor, but also in Init because a user could define a query once and execute it multiple times. - clear_leaf_state(); + deallocate(); + } - if (m_index_matches_destroy) - m_index_matches->destroy(); + void deallocate() noexcept; + void init() override; + size_t find_first_local(size_t start, size_t end) override; - m_index_matches_destroy = false; - m_index_matches.reset(); - m_index_getter.reset(); + virtual std::string describe_condition() const override + { + return Equal::description(); } - void init() override +protected: + inline BinaryData str_to_bin(const StringData& s) noexcept { - deallocate(); - m_dD = 10.0; - StringNodeBase::init(); + return BinaryData(s.data(), s.size()); + } - if (m_column_type == col_type_StringEnum) { - m_dT = 1.0; - m_key_ndx = static_cast(m_condition_column)->get_key_ndx(m_value); - } - else if (m_condition_column->has_search_index()) { - m_dT = 0.0; - } - else { - m_dT = 10.0; - } + virtual void _search_index_init() = 0; + virtual size_t _find_first_local(size_t start, size_t end) = 0; - if (m_condition_column->has_search_index()) { - FindRes fr; - InternalFindResult res; + size_t m_key_ndx = not_found; + size_t m_last_indexed; - if (m_column_type == col_type_StringEnum) { - fr = static_cast(m_condition_column)->find_all_no_copy(m_value, res); - } - else { - fr = static_cast(m_condition_column)->find_all_no_copy(m_value, res); - } + // Used for linear scan through enum-string + SequentialGetter m_cse; - m_index_matches_destroy = false; - m_last_start = size_t(-1); - - switch (fr) { - case FindRes_single: - m_index_matches.reset( - new IntegerColumn(IntegerColumn::unattached_root_tag(), Allocator::get_default())); // Throws - m_index_matches->get_root_array()->create(Array::type_Normal); // Throws - m_index_matches->add(res.payload); - m_index_matches_destroy = true; // we own m_index_matches, so we must destroy it - m_results_start = 0; - m_results_end = 1; - break; - case FindRes_column: - // todo: Apparently we can't use m_index.get_alloc() because it uses default allocator which - // simply makes - // translate(x) = x. Shouldn't it inherit owner column's allocator?! - m_index_matches.reset(new IntegerColumn(m_condition_column->get_alloc(), res.payload)); // Throws - m_results_start = res.start_ndx; - m_results_end = res.end_ndx; - - // FIXME: handle start and end of find_result! - break; - case FindRes_not_found: - m_index_matches.reset(); - m_index_getter.reset(); - break; - } + // Used for index lookup + std::unique_ptr m_index_matches; + bool m_index_matches_destroy = false; + std::unique_ptr> m_index_getter; + size_t m_results_start; + size_t m_results_end; + size_t m_last_start; +}; - if (m_index_matches) { - m_index_getter.reset(new SequentialGetter(m_index_matches.get())); - } - } - else if (m_column_type != col_type_String) { - REALM_ASSERT_DEBUG(dynamic_cast(m_condition_column)); - m_cse.init(static_cast(m_condition_column)); - } - if (m_child) - m_child->init(); - } +// Specialization for Equal condition on Strings - we specialize because we can utilize indexes (if they exist) for +// Equal. +// Future optimization: make specialization for greater, notequal, etc +template <> +class StringNode : public StringNodeEqualBase { +public: + using StringNodeEqualBase::StringNodeEqualBase; - size_t find_first_local(size_t start, size_t end) override - { - REALM_ASSERT(m_table); + void _search_index_init() override; - if (m_condition_column->has_search_index()) { - // Indexed string column - if (!m_index_getter) - return not_found; // no matches in the index + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new StringNode(*this, patches)); + } - if (m_last_start > start) - m_last_indexed = m_results_start; - m_last_start = start; +private: + size_t _find_first_local(size_t start, size_t end) override; +}; - while (m_last_indexed < m_results_end) { - m_index_getter->cache_next(m_last_indexed); - size_t f = m_index_getter->m_leaf_ptr->find_gte(start, m_last_indexed - m_index_getter->m_leaf_start, - m_results_end - m_index_getter->m_leaf_start); - if (f == not_found) { - // Not found in this leaf - move on to next - m_last_indexed = m_index_getter->m_leaf_end; - } - else if (f >= (m_results_end - m_index_getter->m_leaf_start)) { - // Found outside valid range - return not_found; - } - else { - size_t found_index = to_size_t(m_index_getter->m_leaf_ptr->get(f)); - if (found_index >= end) - return not_found; - else { - m_last_indexed = f + m_index_getter->m_leaf_start; - return found_index; - } - } - } - return not_found; +// Specialization for EqualIns condition on Strings - we specialize because we can utilize indexes (if they exist) for +// EqualIns. +template <> +class StringNode : public StringNodeEqualBase { +public: + StringNode(StringData v, size_t column) + : StringNodeEqualBase(v, column) + { + auto upper = case_map(v, true); + auto lower = case_map(v, false); + if (!upper || !lower) { + error_code = "Malformed UTF-8: " + std::string(v); } - - if (m_column_type != col_type_String) { - // Enum string column - if (m_key_ndx == not_found) - return not_found; // not in key set - - for (size_t s = start; s < end; ++s) { - m_cse.cache_next(s); - s = m_cse.m_leaf_ptr->find_first(m_key_ndx, s - m_cse.m_leaf_start, m_cse.local_end(end)); - if (s == not_found) - s = m_cse.m_leaf_end - 1; - else - return s + m_cse.m_leaf_start; - } - - return not_found; + else { + m_ucase = std::move(*upper); + m_lcase = std::move(*lower); } + } - // Normal string column, with long or short leaf - for (size_t s = start; s < end; ++s) { - const StringColumn* asc = static_cast(m_condition_column); - if (s >= m_leaf_end || s < m_leaf_start) { - clear_leaf_state(); - size_t ndx_in_leaf; - m_leaf = asc->get_leaf(s, ndx_in_leaf, m_leaf_type); - m_leaf_start = s - ndx_in_leaf; - if (m_leaf_type == StringColumn::leaf_type_Small) - m_leaf_end = m_leaf_start + static_cast(*m_leaf).size(); - else if (m_leaf_type == StringColumn::leaf_type_Medium) - m_leaf_end = m_leaf_start + static_cast(*m_leaf).size(); - else - m_leaf_end = m_leaf_start + static_cast(*m_leaf).size(); - REALM_ASSERT(m_leaf); - } - size_t end2 = (end > m_leaf_end ? m_leaf_end - m_leaf_start : end - m_leaf_start); - - if (m_leaf_type == StringColumn::leaf_type_Small) - s = static_cast(*m_leaf).find_first(m_value, s - m_leaf_start, end2); - else if (m_leaf_type == StringColumn::leaf_type_Medium) - s = static_cast(*m_leaf).find_first(m_value, s - m_leaf_start, end2); - else - s = static_cast(*m_leaf).find_first(str_to_bin(m_value), true, s - m_leaf_start, - end2); - - if (s == not_found) - s = m_leaf_end - 1; - else - return s + m_leaf_start; - } + void _search_index_init() override; - return not_found; + virtual std::string describe_condition() const override + { + return EqualIns::description(); } std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override { - return std::unique_ptr(new StringNode(*this, patches)); + return std::unique_ptr(new StringNode(*this, patches)); } StringNode(const StringNode& from, QueryNodeHandoverPatches* patches) - : StringNodeBase(from, patches) + : StringNodeEqualBase(from, patches) + , m_ucase(from.m_ucase) + , m_lcase(from.m_lcase) { } private: - inline BinaryData str_to_bin(const StringData& s) noexcept - { - return BinaryData(s.data(), s.size()); - } - - size_t m_key_ndx = not_found; - size_t m_last_indexed; - - // Used for linear scan through enum-string - SequentialGetter m_cse; + std::string m_ucase; + std::string m_lcase; - // Used for index lookup - std::unique_ptr m_index_matches; - bool m_index_matches_destroy = false; - std::unique_ptr> m_index_getter; - size_t m_results_start; - size_t m_results_end; - size_t m_last_start; + size_t _find_first_local(size_t start, size_t end) override; }; + // OR node contains at least two node pointers: Two or more conditions to OR // together in m_conditions, and the next AND condition (if any) in m_child. // // For 'second.equal(23).begin_group().first.equal(111).Or().first.equal(222).end_group().third().equal(555)', this // will first set m_conditions[0] = left-hand-side through constructor, and then later, when .first.equal(222) is -// invoked, -// invocation will set m_conditions[1] = right-hand-side through Query& Query::Or() (see query.cpp). In there, m_child -// is -// also set to next AND condition (if any exists) following the OR. +// invoked, invocation will set m_conditions[1] = right-hand-side through Query& Query::Or() (see query.cpp). +// In there, m_child is also set to next AND condition (if any exists) following the OR. class OrNode : public ParentNode { public: OrNode(std::unique_ptr condition) @@ -1555,8 +1635,37 @@ class OrNode : public ParentNode { } } + void verify_column() const override + { + for (auto& condition : m_conditions) { + condition->verify_column(); + } + } + std::string describe(util::serializer::SerialisationState& state) const override + { + if (m_conditions.size() >= 2) { + + } + std::string s; + for (size_t i = 0; i < m_conditions.size(); ++i) { + if (m_conditions[i]) { + s += m_conditions[i]->describe_expression(state); + if (i != m_conditions.size() - 1) { + s += " or "; + } + } + } + if (m_conditions.size() > 1) { + s = "(" + s + ")"; + } + return s; + } + + void init() override { + ParentNode::init(); + m_dD = 10.0; m_start.clear(); @@ -1574,9 +1683,6 @@ class OrNode : public ParentNode { v.clear(); condition->gather_children(v); } - - if (m_child) - m_child->init(); } size_t find_first_local(size_t start, size_t end) override @@ -1673,8 +1779,15 @@ class NotNode : public ParentNode { m_condition->set_table(*m_table); } + void verify_column() const override + { + m_condition->verify_column(); + } + void init() override { + ParentNode::init(); + m_dD = 10.0; std::vector v; @@ -1687,9 +1800,6 @@ class NotNode : public ParentNode { m_known_range_start = 0; m_known_range_end = 0; m_first_in_known_range = not_found; - - if (m_child) - m_child->init(); } size_t find_first_local(size_t start, size_t end) override; @@ -1711,6 +1821,15 @@ class NotNode : public ParentNode { return ""; } + virtual std::string describe(util::serializer::SerialisationState& state) const override + { + if (m_condition) { + return "!(" + m_condition->describe_expression(state) + ")"; + } + return "!()"; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override { return std::unique_ptr(new NotNode(*this, patches)); @@ -1774,12 +1893,29 @@ class TwoColumnsNode : public ParentNode { m_getter2.init(&get_column(m_condition_column_idx2)); } + void verify_column() const override + { + do_verify_column(m_getter1.m_column, m_condition_column_idx1); + do_verify_column(m_getter2.m_column, m_condition_column_idx2); + } + + virtual std::string describe(util::serializer::SerialisationState& state) const override + { + REALM_ASSERT(m_getter1.m_column != nullptr && m_getter2.m_column != nullptr); + return state.describe_column(ParentNode::m_table, m_getter1.m_column->get_column_index()) + + " " + describe_condition() + " " + + state.describe_column(ParentNode::m_table,m_getter2.m_column->get_column_index()); + } + + virtual std::string describe_condition() const override + { + return TConditionFunction::description(); + } + void init() override { + ParentNode::init(); m_dD = 100.0; - - if (m_child) - m_child->init(); } size_t find_first_local(size_t start, size_t end) override @@ -1840,6 +1976,8 @@ class TwoColumnsNode : public ParentNode { , m_value(from.m_value) , m_condition_column(from.m_condition_column) , m_column_type(from.m_column_type) + , m_condition_column_idx1(from.m_condition_column_idx1) + , m_condition_column_idx2(from.m_condition_column_idx2) { if (m_condition_column) m_condition_column_idx = m_condition_column->get_column_index(); @@ -1864,40 +2002,20 @@ class TwoColumnsNode : public ParentNode { class ExpressionNode : public ParentNode { public: - ExpressionNode(std::unique_ptr expression) - : m_expression(std::move(expression)) - { - m_dD = 10.0; - m_dT = 50.0; - } + ExpressionNode(std::unique_ptr); - void table_changed() override - { - m_expression->set_base_table(m_table.get()); - } + size_t find_first_local(size_t start, size_t end) override; - size_t find_first_local(size_t start, size_t end) override - { - return m_expression->find_first(start, end); - } + void table_changed() override; + void verify_column() const override; - std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override - { - return std::unique_ptr(new ExpressionNode(*this, patches)); - } + virtual std::string describe(util::serializer::SerialisationState& state) const override; - void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override - { - m_expression->apply_handover_patch(patches, group); - ParentNode::apply_handover_patch(patches, group); - } + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override; + void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override; private: - ExpressionNode(const ExpressionNode& from, QueryNodeHandoverPatches* patches) - : ParentNode(from, patches) - , m_expression(from.m_expression->clone(patches)) - { - } + ExpressionNode(const ExpressionNode& from, QueryNodeHandoverPatches* patches); std::unique_ptr m_expression; }; @@ -1925,6 +2043,23 @@ class LinksToNode : public ParentNode { REALM_ASSERT(m_column_type == type_Link || m_column_type == type_LinkList); } + void verify_column() const override + { + do_verify_column(m_column, m_origin_column); + } + + virtual std::string describe(util::serializer::SerialisationState&) const override + { + throw SerialisationError("Serialising a query which links to an object is currently unsupported."); + // We can do something like the following when core gets stable keys + //return describe_column() + " " + describe_condition() + " " + util::serializer::print_value(m_target_row.get_index()); + } + virtual std::string describe_condition() const override + { + return "links to"; + } + + size_t find_first_local(size_t start, size_t end) override { REALM_ASSERT(m_column); diff --git a/Pods/Realm/include/core/realm/query_expression.hpp b/Pods/Realm/include/core/realm/query_expression.hpp index 4ccbfa0..99ffb7d 100644 --- a/Pods/Realm/include/core/realm/query_expression.hpp +++ b/Pods/Realm/include/core/realm/query_expression.hpp @@ -127,9 +127,16 @@ The Columns class encapsulates all this into a simple class that, for any type T #ifndef REALM_QUERY_EXPRESSION_HPP #define REALM_QUERY_EXPRESSION_HPP +#include +#include +#include #include -#include #include +#include +#include +#include +#include +#include #include @@ -149,65 +156,63 @@ T minimum(T a, T b) return a < b ? a : b; } -// FIXME, this needs to exist elsewhere -typedef int64_t Int; -typedef bool Bool; -typedef realm::OldDateTime OldDateTime; -typedef float Float; -typedef double Double; -typedef realm::StringData String; -typedef realm::BinaryData Binary; - #ifdef REALM_OLDQUERY_FALLBACK // Hack to avoid template instantiation errors. See create(). Todo, see if we can simplify only_numeric somehow -namespace { +namespace _impl { + template -T only_numeric(U in) +inline T only_numeric(U in) { return static_cast(util::unwrap(in)); } template -int only_numeric(const StringData&) +inline int only_numeric(const StringData&) { REALM_ASSERT(false); return 0; } template -int only_numeric(const BinaryData&) +inline int only_numeric(const BinaryData&) { REALM_ASSERT(false); return 0; } template -StringData only_string(T in) +inline StringData only_string_op_types(T in) { REALM_ASSERT(false); static_cast(in); return StringData(); } -StringData only_string(StringData in) +inline BinaryData only_string_op_types(BinaryData in) +{ + return in; +} + +template <> +inline StringData only_string_op_types(StringData in) { return in; } template -T no_timestamp(U in) +inline T no_timestamp(U in) { return static_cast(util::unwrap(in)); } template -int no_timestamp(const Timestamp&) +inline int no_timestamp(const Timestamp&) { REALM_ASSERT(false); return 0; } -} // anonymous namespace +} // namespace _impl #endif // REALM_OLDQUERY_FALLBACK @@ -217,6 +222,10 @@ struct Plus { { return v1 + v2; } + static std::string description() + { + return "+"; + } typedef T type; }; @@ -226,6 +235,10 @@ struct Minus { { return v1 - v2; } + static std::string description() + { + return "-"; + } typedef T type; }; @@ -235,6 +248,10 @@ struct Div { { return v1 / v2; } + static std::string description() + { + return "/"; + } typedef T type; }; @@ -244,6 +261,10 @@ struct Mul { { return v1 * v2; } + static std::string description() + { + return "*"; + } typedef T type; }; @@ -254,6 +275,10 @@ struct Pow { { return v * v; } + static std::string description() + { + return "^"; + } typedef T type; }; @@ -318,12 +343,10 @@ struct RowIndex { { return !(*this == other); } - private: util::Optional m_row_index; }; - struct ValueBase { static const size_t default_size = 8; virtual void export_bool(ValueBase& destination) const = 0; @@ -357,7 +380,9 @@ class Expression { virtual size_t find_first(size_t start, size_t end) const = 0; virtual void set_base_table(const Table* table) = 0; + virtual void verify_column() const = 0; virtual const Table* get_base_table() const = 0; + virtual std::string description(util::serializer::SerialisationState& state) const = 0; virtual std::unique_ptr clone(QueryNodeHandoverPatches*) const = 0; virtual void apply_handover_patch(QueryNodeHandoverPatches&, Group&) @@ -393,6 +418,9 @@ class Subexpr { { } + virtual void verify_column() const = 0; + virtual std::string description(util::serializer::SerialisationState& state) const = 0; + // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression // and // binds it to a Query at a later time @@ -421,6 +449,8 @@ template class Operator; template class UnaryOperator; +template +class SizeOperator; template class Compare; template @@ -440,6 +470,8 @@ Query create(L left, const Subexpr2& right) #ifdef REALM_OLDQUERY_FALLBACK // if not defined, then never fallback to query_engine.hpp; always use query_expression const Columns* column = dynamic_cast*>(&right); + // TODO: recognize size operator expressions + // auto size_operator = dynamic_cast, Subexpr>*>(&right); if (column && ((std::numeric_limits::is_integer && std::numeric_limits::is_integer) || (std::is_same::value && std::is_same::value) || @@ -452,37 +484,37 @@ Query create(L left, const Subexpr2& right) Query q = Query(*t); if (std::is_same::value) - q.greater(column->column_ndx(), only_numeric(left)); + q.greater(column->column_ndx(), _impl::only_numeric(left)); else if (std::is_same::value) - q.less(column->column_ndx(), only_numeric(left)); + q.less(column->column_ndx(), _impl::only_numeric(left)); else if (std::is_same::value) q.equal(column->column_ndx(), left); else if (std::is_same::value) q.not_equal(column->column_ndx(), left); else if (std::is_same::value) - q.greater_equal(column->column_ndx(), only_numeric(left)); + q.greater_equal(column->column_ndx(), _impl::only_numeric(left)); else if (std::is_same::value) - q.less_equal(column->column_ndx(), only_numeric(left)); + q.less_equal(column->column_ndx(), _impl::only_numeric(left)); else if (std::is_same::value) - q.equal(column->column_ndx(), only_string(left), false); + q.equal(column->column_ndx(), _impl::only_string_op_types(left), false); else if (std::is_same::value) - q.not_equal(column->column_ndx(), only_string(left), false); + q.not_equal(column->column_ndx(), _impl::only_string_op_types(left), false); else if (std::is_same::value) - q.begins_with(column->column_ndx(), only_string(left)); + q.begins_with(column->column_ndx(), _impl::only_string_op_types(left)); else if (std::is_same::value) - q.begins_with(column->column_ndx(), only_string(left), false); + q.begins_with(column->column_ndx(), _impl::only_string_op_types(left), false); else if (std::is_same::value) - q.ends_with(column->column_ndx(), only_string(left)); + q.ends_with(column->column_ndx(), _impl::only_string_op_types(left)); else if (std::is_same::value) - q.ends_with(column->column_ndx(), only_string(left), false); + q.ends_with(column->column_ndx(), _impl::only_string_op_types(left), false); else if (std::is_same::value) - q.contains(column->column_ndx(), only_string(left)); + q.contains(column->column_ndx(), _impl::only_string_op_types(left)); else if (std::is_same::value) - q.contains(column->column_ndx(), only_string(left), false); + q.contains(column->column_ndx(), _impl::only_string_op_types(left), false); else if (std::is_same::value) - q.like(column->column_ndx(), only_string(left)); + q.like(column->column_ndx(), _impl::only_string_op_types(left)); else if (std::is_same::value) - q.like(column->column_ndx(), only_string(left), false); + q.like(column->column_ndx(), _impl::only_string_op_types(left), false); else { // query_engine.hpp does not support this Cond. Please either add support for it in query_engine.hpp or // fallback to using use 'return new Compare<>' instead. @@ -730,6 +762,40 @@ template <> class Subexpr2 : public Subexpr { }; +template <> +class Subexpr2 : public Subexpr, public Overloads { +public: + Query equal(StringData sd, bool case_sensitive = true); + Query equal(const Subexpr2& col, bool case_sensitive = true); + Query not_equal(StringData sd, bool case_sensitive = true); + Query not_equal(const Subexpr2& col, bool case_sensitive = true); + Query begins_with(StringData sd, bool case_sensitive = true); + Query begins_with(const Subexpr2& col, bool case_sensitive = true); + Query ends_with(StringData sd, bool case_sensitive = true); + Query ends_with(const Subexpr2& col, bool case_sensitive = true); + Query contains(StringData sd, bool case_sensitive = true); + Query contains(const Subexpr2& col, bool case_sensitive = true); + Query like(StringData sd, bool case_sensitive = true); + Query like(const Subexpr2& col, bool case_sensitive = true); +}; + +template <> +class Subexpr2 : public Subexpr, public Overloads { +public: + Query equal(BinaryData sd, bool case_sensitive = true); + Query equal(const Subexpr2& col, bool case_sensitive = true); + Query not_equal(BinaryData sd, bool case_sensitive = true); + Query not_equal(const Subexpr2& col, bool case_sensitive = true); + Query begins_with(BinaryData sd, bool case_sensitive = true); + Query begins_with(const Subexpr2& col, bool case_sensitive = true); + Query ends_with(BinaryData sd, bool case_sensitive = true); + Query ends_with(const Subexpr2& col, bool case_sensitive = true); + Query contains(BinaryData sd, bool case_sensitive = true); + Query contains(const Subexpr2& col, bool case_sensitive = true); + Query like(BinaryData sd, bool case_sensitive = true); + Query like(const Subexpr2& col, bool case_sensitive = true); +}; + /* This class is used to store N values of type T = {int64_t, bool, OldDateTime or StringData}, and allows an entry @@ -775,7 +841,7 @@ struct NullableVector { { if (this != &other) { init(other.m_size); - std::copy_n(other.m_first, other.m_size, m_first); + realm::safe_copy_n(other.m_first, other.m_size, m_first); m_null = other.m_null; } return *this; @@ -784,7 +850,7 @@ struct NullableVector { NullableVector(const NullableVector& other) { init(other.m_size); - std::copy_n(other.m_first, other.m_size, m_first); + realm::safe_copy_n(other.m_first, other.m_size, m_first); m_null = other.m_null; } @@ -828,15 +894,14 @@ struct NullableVector { } template - typename std::enable_if< - realm::is_any::value, - void>::type + typename std::enable_if::value, + void>::type set(size_t index, t_storage value) { m_first[index] = value; } - inline util::Optional get(size_t index) const { if (is_null(index)) @@ -1013,6 +1078,17 @@ inline void NullableVector::set_null(size_t index) m_first[index] = Timestamp{}; } +// ConstTableRef +template <> +inline bool NullableVector::is_null(size_t index) const +{ + return !bool(m_first[index]); +} +template <> +inline void NullableVector::set_null(size_t index) +{ + m_first[index].reset(); +} template struct OperatorOptionalAdapter { @@ -1033,6 +1109,63 @@ struct OperatorOptionalAdapter { } }; + +struct TrueExpression : Expression { + size_t find_first(size_t start, size_t end) const override + { + REALM_ASSERT(start <= end); + if (start != end) + return start; + + return realm::not_found; + } + void set_base_table(const Table*) override + { + } + const Table* get_base_table() const override + { + return nullptr; + } + void verify_column() const override + { + } + std::string description(util::serializer::SerialisationState&) const override + { + return "TRUEPREDICATE"; + } + std::unique_ptr clone(QueryNodeHandoverPatches*) const override + { + return std::unique_ptr(new TrueExpression(*this)); + } +}; + + +struct FalseExpression : Expression { + size_t find_first(size_t, size_t) const override + { + return realm::not_found; + } + void set_base_table(const Table*) override + { + } + void verify_column() const override + { + } + std::string description(util::serializer::SerialisationState&) const override + { + return "FALSEPREDICATE"; + } + const Table* get_base_table() const override + { + return nullptr; + } + std::unique_ptr clone(QueryNodeHandoverPatches*) const override + { + return std::unique_ptr(new FalseExpression(*this)); + } +}; + + // Stores N values of type T. Can also exchange data with other ValueBase of different types template class Value : public ValueBase, public Subexpr2 { @@ -1073,6 +1206,22 @@ class Value : public ValueBase, public Subexpr2 { ValueBase::m_values = values; } + void verify_column() const override + { + } + + virtual std::string description(util::serializer::SerialisationState&) const override + { + if (ValueBase::m_from_link_list) { + return util::serializer::print_value(util::to_string(ValueBase::m_values) + + (ValueBase::m_values == 1 ? " value" : " values")); + } + if (m_storage.m_size > 0) { + return util::serializer::print_value(m_storage[0]); + } + return ""; + } + void evaluate(size_t, ValueBase& destination) override { destination.import(*this); @@ -1553,7 +1702,6 @@ UnaryOperator> power(const Subexpr2& left) return {left.clone()}; } - // Classes used for LinkMap (see below). struct LinkMapFunction { // Your consume() method is given row index of the linked-to table as argument, and you must return whether or @@ -1603,6 +1751,27 @@ struct CountLinks : public LinkMapFunction { size_t m_link_count = 0; }; +struct CountBacklinks : public LinkMapFunction { + CountBacklinks(const Table* t) + : m_table(t) + { + } + + bool consume(size_t row_index) override + { + m_link_count += m_table->get_backlink_count(row_index); + return true; + } + + size_t result() const + { + return m_link_count; + } + + const Table* m_table; + size_t m_link_count = 0; +}; + /* The LinkMap and LinkMapFunction classes are used for query conditions on links themselves (contrary to conditions on @@ -1635,8 +1804,8 @@ class LinkMap { return; m_link_column_indexes.clear(); - const Table* table = m_base_table; - m_base_table = nullptr; + const Table* table = base_table(); + m_tables.clear(); for (auto column : m_link_columns) { m_link_column_indexes.push_back(column->get_column_index()); if (table->get_real_column_type(m_link_column_indexes.back()) == col_type_BackLink) @@ -1648,40 +1817,61 @@ class LinkMap { void set_base_table(const Table* table) { - if (table == m_base_table) + if (table == base_table()) return; - m_base_table = table; + m_tables.clear(); + m_tables.push_back(table); m_link_columns.clear(); m_link_types.clear(); m_only_unary_links = true; for (size_t link_column_index : m_link_column_indexes) { // Link column can be either LinkList or single Link - ColumnType type = table->get_real_column_type(link_column_index); + const Table* t = m_tables.back(); + ColumnType type = t->get_real_column_type(link_column_index); REALM_ASSERT(Table::is_link_type(type) || type == col_type_BackLink); m_link_types.push_back(type); if (type == col_type_LinkList) { - const LinkListColumn& cll = table->get_column_link_list(link_column_index); + const LinkListColumn& cll = t->get_column_link_list(link_column_index); m_link_columns.push_back(&cll); m_only_unary_links = false; - table = &cll.get_target_table(); + m_tables.push_back(&cll.get_target_table()); } else if (type == col_type_Link) { - const LinkColumn& cl = table->get_column_link(link_column_index); + const LinkColumn& cl = t->get_column_link(link_column_index); m_link_columns.push_back(&cl); - table = &cl.get_target_table(); + m_tables.push_back(&cl.get_target_table()); } else if (type == col_type_BackLink) { - const BacklinkColumn& bl = table->get_column_backlink(link_column_index); + const BacklinkColumn& bl = t->get_column_backlink(link_column_index); m_link_columns.push_back(&bl); m_only_unary_links = false; - table = &bl.get_origin_table(); + m_tables.push_back(&bl.get_origin_table()); } } + } + + void verify_columns() const + { + for (size_t i = 0; i < m_link_column_indexes.size(); i++) { + m_tables[i]->verify_column(m_link_column_indexes[i], m_link_columns[i]); + } + } - m_target_table = table; + virtual std::string description(util::serializer::SerialisationState& state) const + { + std::string s; + for (size_t i = 0; i < m_link_column_indexes.size(); ++i) { + if (i < m_tables.size() && m_tables[i]) { + s += state.get_column_name(m_tables[i]->get_table_ref(), m_link_column_indexes[i]); + if (i != m_link_column_indexes.size() - 1) { + s += util::serializer::value_separator; + } + } + } + return s; } std::vector get_links(size_t index) @@ -1698,6 +1888,13 @@ class LinkMap { return counter.result(); } + size_t count_all_backlinks(size_t row) + { + CountBacklinks counter(target_table()); + map_links(row, counter); + return counter.result(); + } + void map_links(size_t row, LinkMapFunction& lm) { map_links(0, row, lm); @@ -1710,12 +1907,18 @@ class LinkMap { const Table* base_table() const { - return m_base_table; + return m_tables.empty() ? nullptr : m_tables[0]; } const Table* target_table() const { - return m_target_table; + REALM_ASSERT(!m_tables.empty()); + return m_tables.back(); + } + + bool links_exist() const + { + return !m_link_columns.empty(); } std::vector m_link_columns; @@ -1778,8 +1981,7 @@ class LinkMap { std::vector m_link_column_indexes; std::vector m_link_types; - const Table* m_base_table = nullptr; - const Table* m_target_table = nullptr; + std::vector m_tables; bool m_only_unary_links = true; template @@ -1787,9 +1989,9 @@ class LinkMap { }; template -Query string_compare(const Columns& left, T right, bool case_insensitive); +Query string_compare(const Subexpr2& left, T right, bool case_insensitive); template -Query string_compare(const Columns& left, const Columns& right, bool case_insensitive); +Query string_compare(const Subexpr2& left, const Subexpr2& right, bool case_insensitive); template Value make_value_for_link(bool only_unary_links, size_t size) @@ -1842,6 +2044,17 @@ class SimpleQuerySupport : public Subexpr2 { } } + void verify_column() const override + { + // verify links + m_link_map.verify_columns(); + // verify target table + const Table* target_table = m_link_map.target_table(); + if (target_table && m_column_ndx != npos) { + target_table->verify_column(m_column_ndx, m_column); + } + } + void evaluate(size_t index, ValueBase& destination) override { Value& d = static_cast&>(destination); @@ -1871,6 +2084,11 @@ class SimpleQuerySupport : public Subexpr2 { return m_link_map.m_link_columns.size() > 0; } + virtual std::string description(util::serializer::SerialisationState& state) const override + { + return state.describe_columns(m_link_map, m_column_ndx); + } + std::unique_ptr clone(QueryNodeHandoverPatches* patches = nullptr) const override { return make_subexpr>(static_cast&>(*this), patches); @@ -1893,6 +2111,11 @@ class SimpleQuerySupport : public Subexpr2 { return m_column->get_column_index(); } + SizeOperator> size() + { + return SizeOperator>(this->clone(nullptr)); + } + private: // Column index of payload column of m_table mutable size_t m_column_ndx; @@ -1911,76 +2134,27 @@ class Columns : public SimpleQuerySupport { using SimpleQuerySupport::SimpleQuerySupport; }; - template <> class Columns : public SimpleQuerySupport { public: - using SimpleQuerySupport::SimpleQuerySupport; - - Query equal(StringData sd, bool case_sensitive = true) - { - return string_compare(*this, sd, case_sensitive); - } - - Query equal(const Columns& col, bool case_sensitive = true) - { - return string_compare(*this, col, case_sensitive); - } - - Query not_equal(StringData sd, bool case_sensitive = true) - { - return string_compare(*this, sd, case_sensitive); - } - - Query not_equal(const Columns& col, bool case_sensitive = true) - { - return string_compare(*this, col, case_sensitive); - } - - Query begins_with(StringData sd, bool case_sensitive = true) - { - return string_compare(*this, sd, case_sensitive); - } - - Query begins_with(const Columns& col, bool case_sensitive = true) - { - return string_compare(*this, col, case_sensitive); - } - - Query ends_with(StringData sd, bool case_sensitive = true) - { - return string_compare(*this, sd, case_sensitive); - } - - Query ends_with(const Columns& col, bool case_sensitive = true) - { - return string_compare(*this, col, case_sensitive); - } - - Query contains(StringData sd, bool case_sensitive = true) - { - return string_compare(*this, sd, case_sensitive); - } - - Query contains(const Columns& col, bool case_sensitive = true) + Columns(size_t column, const Table* table, std::vector links = {}) + : SimpleQuerySupport(column, table, links) { - return string_compare(*this, col, case_sensitive); } - Query like(StringData sd, bool case_sensitive = true) + Columns(Columns const& other, QueryNodeHandoverPatches* patches = nullptr) + : SimpleQuerySupport(other, patches) { - return string_compare(*this, sd, case_sensitive); } - Query like(const Columns& col, bool case_sensitive = true) + Columns(Columns&& other) + : SimpleQuerySupport(other) { - return string_compare(*this, col, case_sensitive); } }; - template -Query string_compare(const Columns& left, T right, bool case_sensitive) +Query string_compare(const Subexpr2& left, T right, bool case_sensitive) { StringData sd(right); if (case_sensitive) @@ -1990,7 +2164,7 @@ Query string_compare(const Columns& left, T right, bool case_sensiti } template -Query string_compare(const Columns& left, const Columns& right, bool case_sensitive) +Query string_compare(const Subexpr2& left, const Subexpr2& right, bool case_sensitive) { if (case_sensitive) return make_expression>(right.clone(), left.clone()); @@ -1998,6 +2172,26 @@ Query string_compare(const Columns& left, const Columns& return make_expression>(right.clone(), left.clone()); } +template +Query binary_compare(const Subexpr2& left, T right, bool case_sensitive) +{ + BinaryData data(right); + if (case_sensitive) + return create(data, left); + else + return create(data, left); +} + +template +Query binary_compare(const Subexpr2& left, const Subexpr2& right, bool case_sensitive) +{ + if (case_sensitive) + return make_expression>(right.clone(), left.clone()); + else + return make_expression>(right.clone(), left.clone()); +} + + // Columns == Columns inline Query operator==(const Columns& left, const Columns& right) { @@ -2078,6 +2272,11 @@ class UnaryLinkCompare : public Expression { m_link_map.set_base_table(table); } + void verify_column() const override + { + m_link_map.verify_columns(); + } + // Return main table of query (table on which table->where()... is invoked). Note that this is not the same as // any linked-to payload tables const Table* get_base_table() const override @@ -2088,9 +2287,6 @@ class UnaryLinkCompare : public Expression { size_t find_first(size_t start, size_t end) const override { for (; start < end;) { - std::vector l = m_link_map.get_links(start); - // We have found a Link which is NULL, or LinkList with 0 entries. Return it as match. - FindNullLinks fnl; m_link_map.map_links(start, fnl); if (fnl.m_has_link == has_links) @@ -2102,6 +2298,11 @@ class UnaryLinkCompare : public Expression { return not_found; } + virtual std::string description(util::serializer::SerialisationState& state) const override + { + return state.describe_columns(m_link_map, realm::npos) + (has_links ? " != NULL" : " == NULL"); + } + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override { return std::unique_ptr(new UnaryLinkCompare(*this, patches)); @@ -2144,77 +2345,245 @@ class LinkCount : public Subexpr2 { m_link_map.set_base_table(table); } + void verify_column() const override + { + m_link_map.verify_columns(); + } + void evaluate(size_t index, ValueBase& destination) override { size_t count = m_link_map.count_links(index); destination.import(Value(false, 1, count)); } + virtual std::string description(util::serializer::SerialisationState& state) const override + { + return state.describe_columns(m_link_map, realm::npos) + util::serializer::value_separator + "@count"; + } + private: LinkMap m_link_map; }; -struct ConstantRowValueHandoverPatch : public QueryNodeHandoverPatch { - std::unique_ptr row_patch; -}; - -class ConstantRowValue : public Subexpr2 { +// Gives a count of all backlinks across all columns for the specified row. +// The unused template parameter is a hack to avoid a circular dependency between table.hpp and query_expression.hpp. +template +class BacklinkCount : public Subexpr2 { public: - ConstantRowValue(const ConstRow& row) - : m_row(row) + BacklinkCount(LinkMap link_map) + : m_link_map(std::move(link_map)) + { + } + BacklinkCount(const Table* table, std::vector links = {}) + : m_link_map(table, std::move(links)) + { + } + BacklinkCount(BacklinkCount const& other, QueryNodeHandoverPatches* patches) + : Subexpr2(other) + , m_link_map(other.m_link_map, patches) { } - void set_base_table(const Table*) override + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override { + return make_subexpr >(*this, patches); } + const Table* get_base_table() const override { - return nullptr; + return m_link_map.base_table(); } - void evaluate(size_t, ValueBase& destination) override + void set_base_table(const Table* table) override { - if (m_row.is_attached()) { - Value v(RowIndex(m_row.get_index())); - destination.import(v); + m_link_map.set_base_table(table); + } + + void verify_column() const override + { + m_link_map.verify_columns(); + } + + void evaluate(size_t index, ValueBase& destination) override + { + size_t count; + if (m_link_map.links_exist()) { + count = m_link_map.count_all_backlinks(index); } else { - Value v(RowIndex::Detached); - destination.import(v); + count = m_link_map.target_table()->get_backlink_count(index); } + destination.import(Value(false, 1, count)); } - std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + virtual std::string description(util::serializer::SerialisationState& state) const override { - return std::unique_ptr(new ConstantRowValue(*this, patches)); + std::string s; + if (m_link_map.links_exist()) { + s += state.describe_columns(m_link_map, realm::npos) + util::serializer::value_separator; + } + s += "@links.@count"; + return s; } +private: + LinkMap m_link_map; +}; - void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override - { - REALM_ASSERT(patches.size()); - std::unique_ptr abstract_patch = std::move(patches.back()); - patches.pop_back(); - auto patch = dynamic_cast(abstract_patch.get()); - REALM_ASSERT(patch); +template +class SizeOperator : public Subexpr2 { +public: + SizeOperator(std::unique_ptr left) + : m_expr(std::move(left)) + { + } - m_row.apply_and_consume_patch(patch->row_patch, group); + // See comment in base class + void set_base_table(const Table* table) override + { + m_expr->set_base_table(table); } -private: - ConstantRowValue(const ConstantRowValue& source, QueryNodeHandoverPatches* patches) - : m_row(patches ? ConstRow() : source.m_row) + void verify_column() const override { - if (!patches) - return; + m_expr->verify_column(); + } - std::unique_ptr patch(new ConstantRowValueHandoverPatch); - ConstRow::generate_patch(source.m_row, patch->row_patch); - patches->emplace_back(patch.release()); + // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression + // and binds it to a Query at a later time + const Table* get_base_table() const override + { + return m_expr->get_base_table(); } - ConstRow m_row; + // destination = operator(left) + void evaluate(size_t index, ValueBase& destination) override + { + REALM_ASSERT_DEBUG(dynamic_cast*>(&destination) != nullptr); + Value* d = static_cast*>(&destination); + REALM_ASSERT(d); + + Value v; + m_expr->evaluate(index, v); + + size_t sz = v.m_values; + d->init(v.m_from_link_list, sz); + + for (size_t i = 0; i < sz; i++) { + auto elem = v.m_storage.get(i); + if (!elem) { + d->m_storage.set_null(i); + } + else { + d->m_storage.set(i, oper()(*elem)); + } + } + } + + std::string description(util::serializer::SerialisationState& state) const override + { + if (m_expr) { + return m_expr->description(state) + util::serializer::value_separator + "@size"; + } + return "@size"; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new SizeOperator(*this, patches)); + } + + void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override + { + m_expr->apply_handover_patch(patches, group); + } + +private: + SizeOperator(const SizeOperator& other, QueryNodeHandoverPatches* patches) + : m_expr(other.m_expr->clone(patches)) + { + } + + typedef typename oper::type T; + std::unique_ptr m_expr; +}; + +struct ConstantRowValueHandoverPatch : public QueryNodeHandoverPatch { + std::unique_ptr row_patch; +}; + +class ConstantRowValue : public Subexpr2 { +public: + ConstantRowValue(const ConstRow& row) + : m_row(row) + { + } + + void set_base_table(const Table*) override + { + } + + void verify_column() const override + { + } + + const Table* get_base_table() const override + { + return nullptr; + } + + void evaluate(size_t, ValueBase& destination) override + { + if (m_row.is_attached()) { + Value v(RowIndex(m_row.get_index())); + destination.import(v); + } + else { + Value v(RowIndex::Detached); + destination.import(v); + } + } + + virtual std::string description(util::serializer::SerialisationState&) const override + { + throw SerialisationError("Serialising a query which links to an object is currently unsupported."); + // TODO: we can do something like the following when core gets stable keys: + //if (!m_row.is_attached()) { + // return util::serializer::print_value("detached object"); + //} + //return util::serializer::print_value(m_row.get_index()); + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new ConstantRowValue(*this, patches)); + } + + void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override + { + REALM_ASSERT(patches.size()); + std::unique_ptr abstract_patch = std::move(patches.back()); + patches.pop_back(); + + auto patch = dynamic_cast(abstract_patch.get()); + REALM_ASSERT(patch); + + m_row.apply_and_consume_patch(patch->row_patch, group); + } + +private: + ConstantRowValue(const ConstantRowValue& source, QueryNodeHandoverPatches* patches) + : m_row(patches ? ConstRow() : source.m_row) + { + if (!patches) + return; + + std::unique_ptr patch(new ConstantRowValueHandoverPatch); + ConstRow::generate_patch(source.m_row, patch->row_patch); + patches->emplace_back(patch.release()); + } + + ConstRow m_row; }; template @@ -2245,6 +2614,12 @@ class Columns : public Subexpr2 { return LinkCount(m_link_map); } + template + BacklinkCount backlink_count() const + { + return BacklinkCount(m_link_map); + } + template SubColumns column(size_t column_ndx) const { @@ -2265,39 +2640,329 @@ class Columns : public Subexpr2 { m_link_map.set_base_table(table); } - std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + void verify_column() const override { - return make_subexpr>(*this, patches); + m_link_map.verify_columns(); } - void evaluate(size_t index, ValueBase& destination) override + std::string description(util::serializer::SerialisationState& state) const override { - std::vector links = m_link_map.get_links(index); - Value v = make_value_for_link(m_link_map.only_unary_links(), links.size()); + return state.describe_columns(m_link_map, realm::npos); + } - for (size_t t = 0; t < links.size(); t++) { - v.m_storage.set(t, RowIndex(links[t])); - } - destination.import(v); + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new Columns(*this, patches)); } + void evaluate(size_t index, ValueBase& destination) override; + + +private: + LinkMap m_link_map; + friend class Table; + + Columns(size_t column_ndx, const Table* table, const std::vector& links = {}) + : m_link_map(table, links) + { + static_cast(column_ndx); + } Columns(const Columns& other, QueryNodeHandoverPatches* patches) : Subexpr2(other) , m_link_map(other.m_link_map, patches) { } +}; + +template +class ListColumns; +template +class ListColumnAggregate; +namespace aggregate_operations { +template +class Minimum; +template +class Maximum; +template +class Sum; +template +class Average; +} + +template <> +class Columns : public Subexpr2 { +public: + const Table* get_base_table() const override + { + return m_link_map.base_table(); + } + + void set_base_table(const Table* table) override + { + m_link_map.set_base_table(table); + m_column = &m_link_map.target_table()->get_column_table(m_column_ndx); + } + + void verify_column() const override + { + m_link_map.verify_columns(); + m_link_map.target_table()->verify_column(m_column_ndx, m_column); + } + + std::string description(util::serializer::SerialisationState&) const override + { + throw SerialisationError("Serialisation of query expressions involving subtables is not yet supported."); + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new Columns(*this, patches)); + } + + void evaluate(size_t index, ValueBase& destination) override + { + evaluate_internal(index, destination, ValueBase::default_size); + } + + void evaluate_internal(size_t index, ValueBase& destination, size_t nb_elements); + + template + ListColumns column(size_t ndx) const + { + return ListColumns(ndx, Columns(*this, nullptr)); + } + + template + ListColumns list() const + { + return column(0); + } + + SizeOperator> size() + { + return SizeOperator>(this->clone(nullptr)); + } private: + LinkMap m_link_map; + size_t m_column_ndx; + const SubtableColumn* m_column = nullptr; + friend class Table; + template + friend class ListColumnsBase; + template + friend class ListColumnAggregate; + Columns(size_t column_ndx, const Table* table, const std::vector& links = {}) : m_link_map(table, links) + , m_column_ndx(column_ndx) + , m_column(&m_link_map.target_table()->get_column_table(column_ndx)) { - static_cast(column_ndx); } - LinkMap m_link_map; - friend class Table; + Columns(const Columns& other, QueryNodeHandoverPatches* patches) + : Subexpr2(other) + , m_link_map(other.m_link_map, patches) + , m_column_ndx(other.m_column_ndx) + , m_column(other.m_column) + { + if (m_column && patches) + m_column_ndx = m_column->get_column_index(); + } +}; + +template +class ListColumnsBase : public Subexpr2 { +public: + ListColumnsBase(size_t column_ndx, Columns column) + : m_column_ndx(column_ndx) + , m_subtable_column(std::move(column)) + { + } + + ListColumnsBase(const ListColumnsBase& other, QueryNodeHandoverPatches* patches) + : m_column_ndx(other.m_column_ndx) + , m_subtable_column(other.m_subtable_column, patches) + { + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return make_subexpr>(*this, patches); + } + + const Table* get_base_table() const override + { + return m_subtable_column.get_base_table(); + } + + void set_base_table(const Table* table) override + { + m_subtable_column.set_base_table(table); + } + + void verify_column() const override + { + m_subtable_column.verify_column(); + } + + void evaluate(size_t index, ValueBase& destination) override + { + Value subtables; + m_subtable_column.evaluate_internal(index, subtables, 1); + size_t sz = 0; + for (size_t i = 0; i < subtables.m_values; i++) { + auto val = subtables.m_storage[i]; + if (val) + sz += val->size(); + } + auto v = make_value_for_link::type>(false, sz); + size_t k = 0; + for (size_t i = 0; i < subtables.m_values; i++) { + auto table = subtables.m_storage[i]; + if (table) { + size_t s = table->size(); + for (size_t j = 0; j < s; j++) { + if (!table->is_null(m_column_ndx, j)) { + v.m_storage.set(k++, table->get(m_column_ndx, j)); + } + } + } + } + destination.import(v); + } + + virtual std::string description(util::serializer::SerialisationState&) const override + { + throw SerialisationError("Serialisation of subtable expressions is not yet supported."); + } + + ListColumnAggregate> min() const + { + return {m_column_ndx, m_subtable_column}; + } + + ListColumnAggregate> max() const + { + return {m_column_ndx, m_subtable_column}; + } + + ListColumnAggregate> sum() const + { + return {m_column_ndx, m_subtable_column}; + } + + ListColumnAggregate> average() const + { + return {m_column_ndx, m_subtable_column}; + } + + +private: + // Storing the column index here could be a potential problem if the column + // changes id due to insertion/deletion. + size_t m_column_ndx; + Columns m_subtable_column; }; +template +class ListColumns : public ListColumnsBase { +public: + using ListColumnsBase::ListColumnsBase; +}; + +template <> +class ListColumns : public ListColumnsBase { +public: + ListColumns(size_t column_ndx, Columns column) + : ListColumnsBase(column_ndx, column) + { + } + + ListColumns(const ListColumnsBase& other, QueryNodeHandoverPatches* patches) + : ListColumnsBase(other, patches) + { + } + + ListColumns(ListColumns&& other) + : ListColumnsBase(other) + { + } +}; + +template +class ListColumnAggregate : public Subexpr2 { +public: + using R = typename Operation::ResultType; + + ListColumnAggregate(size_t column_ndx, Columns column) + : m_column_ndx(column_ndx) + , m_subtable_column(std::move(column)) + { + } + + ListColumnAggregate(const ListColumnAggregate& other, QueryNodeHandoverPatches* patches) + : m_column_ndx(other.m_column_ndx) + , m_subtable_column(other.m_subtable_column, patches) + { + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return make_subexpr(*this, patches); + } + + const Table* get_base_table() const override + { + return m_subtable_column.get_base_table(); + } + + void set_base_table(const Table* table) override + { + m_subtable_column.set_base_table(table); + } + + void verify_column() const override + { + m_subtable_column.verify_column(); + } + + void evaluate(size_t index, ValueBase& destination) override + { + Value subtables; + m_subtable_column.evaluate_internal(index, subtables, 1); + REALM_ASSERT_DEBUG(subtables.m_values > 0 || subtables.m_from_link_list); + size_t sz = subtables.m_values; + // The result is an aggregate value for each table + auto v = make_value_for_link(!subtables.m_from_link_list, sz); + for (unsigned i = 0; i < sz; i++) { + auto table = subtables.m_storage[i]; + Operation op; + if (table) { + size_t s = table->size(); + for (unsigned j = 0; j < s; j++) { + op.accumulate(table->get(m_column_ndx, j)); + } + } + if (op.is_null()) { + v.m_storage.set_null(i); + } + else { + v.m_storage.set(i, op.result()); + } + } + destination.import(v); + } + + virtual std::string description(util::serializer::SerialisationState&) const override + { + throw SerialisationError("Serialisation of queries involving subtable expressions is not yet supported."); + } + +private: + size_t m_column_ndx; + Columns m_subtable_column; +}; template Query compare(const Subexpr2& left, const ConstRow& row) @@ -2381,21 +3046,21 @@ class Columns : public Subexpr2 { Columns(size_t column, const Table* table, std::vector links = {}) : m_link_map(table, std::move(links)) - , m_column(column) - , m_nullable(m_link_map.target_table()->is_nullable(m_column)) + , m_column_ndx(column) + , m_nullable(m_link_map.target_table()->is_nullable(m_column_ndx)) { } Columns(const Columns& other, QueryNodeHandoverPatches* patches = nullptr) : m_link_map(other.m_link_map, patches) - , m_column(other.m_column) + , m_column_ndx(other.m_column_ndx) , m_nullable(other.m_nullable) { if (!other.m_sg) return; if (patches) { - m_column = other.get_column_base().get_column_index(); + m_column_ndx = other.get_column_base().get_column_index(); } else { if (m_nullable && std::is_same::value) { @@ -2412,7 +3077,7 @@ class Columns : public Subexpr2 { if (this != &other) { m_link_map = other.m_link_map; m_sg.reset(); - m_column = other.m_column; + m_column_ndx = other.m_column_ndx; m_nullable = other.m_nullable; } return *this; @@ -2430,9 +3095,9 @@ class Columns : public Subexpr2 { return; m_link_map.set_base_table(table); - m_nullable = m_link_map.target_table()->is_nullable(m_column); + m_nullable = m_link_map.target_table()->is_nullable(m_column_ndx); - const ColumnBase* c = &m_link_map.target_table()->get_column_base(m_column); + const ColumnBase* c = &m_link_map.target_table()->get_column_base(m_column_ndx); if (m_nullable && std::is_same::value) { init(c); } @@ -2441,6 +3106,17 @@ class Columns : public Subexpr2 { } } + void verify_column() const override + { + // verify links + m_link_map.verify_columns(); + // verify target table + const Table* target_table = m_link_map.target_table(); + if (target_table && m_column_ndx != npos) { + target_table->verify_column(m_column_ndx, &get_column_base()); + } + } + template void init(const ColumnBase* c) { @@ -2502,9 +3178,7 @@ class Columns : public Subexpr2 { REALM_ASSERT_3(ValueBase::default_size, ==, 8); auto sgc_2 = static_cast*>(m_sg.get()); - sgc_2->m_leaf_ptr->get_chunk( - index - sgc->m_leaf_start, - static_cast*>(static_cast(&v))->m_storage.m_first); + sgc_2->m_leaf_ptr->get_chunk(index - sgc->m_leaf_start, v.m_storage.m_first); destination.import(v); } @@ -2522,6 +3196,11 @@ class Columns : public Subexpr2 { } } + virtual std::string description(util::serializer::SerialisationState& state) const override + { + return state.describe_columns(m_link_map, m_column_ndx); + } + // Load values from Column into destination void evaluate(size_t index, ValueBase& destination) override { @@ -2545,7 +3224,7 @@ class Columns : public Subexpr2 { size_t column_ndx() const noexcept { - return m_sg ? get_column_base().get_column_index() : m_column; + return m_sg ? get_column_base().get_column_index() : m_column_ndx; } private: @@ -2555,7 +3234,7 @@ class Columns : public Subexpr2 { std::unique_ptr m_sg; // Column index of payload column of m_table - size_t m_column; + size_t m_column_ndx; // set to false by default for stand-alone Columns declaration that are not yet associated with any table // or oclumn. Call init() to update it or use a constructor that takes table + column index as argument. @@ -2572,16 +3251,6 @@ class Columns : public Subexpr2 { template class SubColumnAggregate; -namespace aggregate_operations { -template -class Minimum; -template -class Maximum; -template -class Sum; -template -class Average; -} template class SubColumns : public Subexpr { @@ -2608,12 +3277,23 @@ class SubColumns : public Subexpr { m_column.set_base_table(m_link_map.target_table()); } + void verify_column() const override + { + m_link_map.verify_columns(); + m_column.verify_column(); + } + void evaluate(size_t, ValueBase&) override { // SubColumns can only be used in an expression in conjunction with its aggregate methods. REALM_ASSERT(false); } + virtual std::string description(util::serializer::SerialisationState&) const override + { + return ""; // by itself there are no conditions, see SubColumnAggregate + } + SubColumnAggregate> min() const { return {m_column, m_link_map}; @@ -2669,6 +3349,12 @@ class SubColumnAggregate : public Subexpr2 { m_column.set_base_table(m_link_map.target_table()); } + void verify_column() const override + { + m_link_map.verify_columns(); + m_column.verify_column(); + } + void evaluate(size_t index, ValueBase& destination) override { std::vector links = m_link_map.get_links(index); @@ -2704,6 +3390,12 @@ class SubColumnAggregate : public Subexpr2 { } } + virtual std::string description(util::serializer::SerialisationState& state) const override + { + util::serializer::SerialisationState empty_state; + return state.describe_columns(m_link_map, realm::npos) + util::serializer::value_separator + Operation::description() + util::serializer::value_separator + m_column.description(empty_state); + } + private: Columns m_column; LinkMap m_link_map; @@ -2731,6 +3423,11 @@ class SubQueryCount : public Subexpr2 { m_link_map.set_base_table(table); } + void verify_column() const override + { + m_link_map.verify_columns(); + } + void evaluate(size_t index, ValueBase& destination) override { std::vector links = m_link_map.get_links(index); @@ -2743,6 +3440,18 @@ class SubQueryCount : public Subexpr2 { destination.import(Value(false, 1, size_t(count))); } + virtual std::string description(util::serializer::SerialisationState& state) const override + { + REALM_ASSERT(m_link_map.base_table() != nullptr); + std::string target = state.describe_columns(m_link_map, realm::npos); + std::string var_name = state.get_variable_name(m_link_map.base_table()->get_table_ref()); + state.subquery_prefix_list.push_back(var_name); + std::string desc = "SUBQUERY(" + target + ", " + var_name + ", " + m_query.get_description(state) + ")" + + util::serializer::value_separator + "@count"; + state.subquery_prefix_list.pop_back(); + return desc; + } + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override { if (patches) @@ -2837,6 +3546,10 @@ class Minimum : public BaseAggregateOperation> { { return std::min(a, b); } + static std::string description() + { + return "@min"; + } }; template @@ -2850,6 +3563,10 @@ class Maximum : public BaseAggregateOperation> { { return std::max(a, b); } + static std::string description() + { + return "@max"; + } }; template @@ -2867,6 +3584,10 @@ class Sum : public BaseAggregateOperation> { { return false; } + static std::string description() + { + return "@sum"; + } }; template @@ -2886,6 +3607,11 @@ class Average : public BaseAggregateOperation, double> { { return Base::m_result / Base::m_count; } + static std::string description() + { + return "@avg"; + } + }; } @@ -2919,9 +3645,13 @@ class UnaryOperator : public Subexpr2 { m_left->set_base_table(table); } + void verify_column() const override + { + m_left->verify_column(); + } + // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression - // and - // binds it to a Query at a later time + // and binds it to a Query at a later time const Table* get_base_table() const override { return m_left->get_base_table(); @@ -2937,6 +3667,14 @@ class UnaryOperator : public Subexpr2 { destination.import(result); } + virtual std::string description(util::serializer::SerialisationState& state) const override + { + if (m_left) { + return m_left->description(state); + } + return ""; + } + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override { return make_subexpr(*this, patches); @@ -2987,6 +3725,12 @@ class Operator : public Subexpr2 { m_right->set_base_table(table); } + void verify_column() const override + { + m_left->verify_column(); + m_right->verify_column(); + } + // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression // and // binds it to a Query at a later time @@ -3014,6 +3758,19 @@ class Operator : public Subexpr2 { destination.import(result); } + virtual std::string description(util::serializer::SerialisationState& state) const override + { + std::string s; + if (m_left) { + s += m_left->description(state); + } + s += (" " + oper::description() + " "); + if (m_right) { + s += m_right->description(state); + } + return s; + } + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override { return make_subexpr(*this, patches); @@ -3048,6 +3805,12 @@ class Compare : public Expression { m_right->set_base_table(table); } + void verify_column() const override + { + m_left->verify_column(); + m_right->verify_column(); + } + // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression // and // binds it to a Query at a later time @@ -3085,6 +3848,25 @@ class Compare : public Expression { return not_found; // no match } + virtual std::string description(util::serializer::SerialisationState& state) const override + { + if (std::is_same::value + || std::is_same::value + || std::is_same::value + || std::is_same::value + || std::is_same::value + || std::is_same::value + || std::is_same::value + || std::is_same::value) { + // these string conditions have the arguments reversed but the order is important + // operations ==, and != can be reversed because the produce the same results both ways + return util::serializer::print_value(m_right->description(state) + " " + TCond::description() + + " " + m_left->description(state)); + } + return util::serializer::print_value(m_left->description(state) + " " + TCond::description() + + " " + m_right->description(state)); + } + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override { return std::unique_ptr(new Compare(*this, patches)); diff --git a/Pods/Realm/include/core/realm/query_operators.hpp b/Pods/Realm/include/core/realm/query_operators.hpp new file mode 100644 index 0000000..203af84 --- /dev/null +++ b/Pods/Realm/include/core/realm/query_operators.hpp @@ -0,0 +1,71 @@ +/************************************************************************* + * + * Copyright 2017 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_QUERY_OPERATORS_HPP +#define REALM_QUERY_OPERATORS_HPP + +#include +#include +#include +#include + +namespace realm { + +// This is not supported in the general case +template +struct Size; + +template <> +struct Size { + int64_t operator()(StringData v) const + { + return v.size(); + } + typedef StringData type; +}; + +template <> +struct Size { + int64_t operator()(BinaryData v) const + { + return v.size(); + } + typedef BinaryData type; +}; + +template <> +struct Size { + int64_t operator()(ConstTableRef v) const + { + return v->size(); + } + typedef ConstTableRef type; +}; + +template <> +struct Size { + int64_t operator()(ConstLinkViewRef v) const + { + return v->size(); + } + typedef ConstLinkViewRef type; +}; + +} // namespace realm + +#endif // REALM_QUERY_OPERATORS_HPP diff --git a/Pods/Realm/include/core/realm/replication.hpp b/Pods/Realm/include/core/realm/replication.hpp index 857ab2a..f68a9af 100644 --- a/Pods/Realm/include/core/realm/replication.hpp +++ b/Pods/Realm/include/core/realm/replication.hpp @@ -26,11 +26,10 @@ #include #include -#include #include #include #include -#include +#include #include namespace realm { @@ -218,21 +217,30 @@ class Replication : public _impl::TransactLogConvenientEncoder, protected _impl: /// or ended prematurely. static void apply_changeset(InputStream& changeset, Group& group, util::Logger* logger = nullptr); + /// CAUTION: These values are stored in Realm files, so value reassignment + /// is not allowed. enum HistoryType { /// No history available. No support for either continuous transactions /// or inter-client synchronization. hist_None = 0, /// Out-of-Realm history supporting continuous transactions. + /// + /// NOTE: This history type is no longer in use. The value needs to stay + /// reserved in case someone tries to open an old Realm file. hist_OutOfRealm = 1, /// In-Realm history supporting continuous transactions - /// (_impl::InRealmHistory). + /// (make_in_realm_history()). hist_InRealm = 2, - /// In-Realm history supporting continuous transactions and inter-client - /// synchronization (_impl::SyncHistory). - hist_Sync = 3 + /// In-Realm history supporting continuous transactions and client-side + /// synchronization protocol (realm::sync::ClientHistory). + hist_SyncClient = 3, + + /// In-Realm history supporting continuous transactions and server-side + /// synchronization protocol (realm::_impl::ServerHistory). + hist_SyncServer = 4 }; /// Returns the type of history maintained by this Replication @@ -243,115 +251,81 @@ class Replication : public _impl::TransactLogConvenientEncoder, protected _impl: /// history, at the beginning of a new session. /// /// As a special case, if there is no top array (Group::m_top) at the - /// beginning of a new session, then all history types (as returned by - /// get_history_type()) are allowed during that session. Note that this is - /// only possible if there was no preceding session, or if no transaction - /// was sucessfully comitted during any of the preceding sessions. As soon - /// as a transaction is successfully committed, the Realm contains at least - /// a top array, and from that point on, the history type is generally - /// fixed, although still subject to certain allowed changes (as mentioned - /// below). + /// beginning of a new session, then the history type is still undecided and + /// all history types (as returned by get_history_type()) are threfore + /// allowed for the session initiator. Note that this case only arises if + /// there was no preceding session, or if no transaction was sucessfully + /// committed during any of the preceding sessions. As soon as a transaction + /// is successfully committed, the Realm contains at least a top array, and + /// from that point on, the history type is generally fixed, although still + /// subject to certain allowed changes (as mentioned below). /// /// For the sake of backwards compatibility with older Realm files that does /// not store any history type, the following rule shall apply: /// /// - If the top array of a Realm file (Group::m_top) does not contain a /// history type, because it is too short, it shall be understood as - /// implicitely storing the type \ref hist_None. + /// implicitly storing the type \ref hist_None. /// /// Note: In what follows, the meaning of *preceding session* is: The last /// preceding session that modified the Realm by sucessfully committing a /// new snapshot. /// - /// Older Realm files do not store any history type, even when they were - /// last used with a history of type \ref hist_OutOfRealm. Howewver, since - /// such histories (\ref hist_OutOfRealm) are placed outside the Realm file, - /// and are transient (recreated at the beginning of each new session), a - /// new session is not obliged to use the same type of history (\ref - /// hist_OutOfRealm). For this reason, and to achieve further backwards - /// compatibility, the following rules are adopted: - /// - /// - At the beginning of a new session, if there is no stored history - /// type (no top array), or if the stored history type is \ref - /// hist_None, assume that the history type used during the preceding - /// session was \ref hist_None or \ref hist_OutOfRealm, or that there - /// was no preceding session. In all other cases, assume that the stored - /// history type is the type used during the preceding session. - /// - /// - When storing the history type, store \ref hist_None if the history - /// type used in the current session is \ref hist_None or \ref - /// hist_OutOfRealm. In all other cases, store the actual history type - /// used. - /// /// It shall be allowed to switch to a \ref hist_InRealm history if the - /// stored history type is either \ref hist_None or \ref - /// hist_OutOfRealm. Fortunately, this can be done simply by adding a - /// history to the Realm file (of type \ref hist_InRealm), and that is - /// possible because a \ref hist_InRealm history is independent of any - /// history used in a previous session (as long as it was session-confined), - /// or whether any history was used at all. Conversely, if a \ref - /// hist_OutOfRealm history was used in the previous session, then the - /// contents of that history becomes obsolete at the end of the previous - /// session. + /// stored history type is \ref hist_None. This can be done simply by adding + /// a new history to the Realm file. This is possible because histories of + /// this type a transient in nature, and need not survive from one session + /// to the next. /// /// On the other hand, as soon as a history of type \ref hist_InRealm is /// added to a Realm file, that history type is binding for all subsequent /// sessions. In theory, this constraint is not necessary, and a later - /// switch to \ref hist_None or \ref hist_OutOfRealm would be possible - /// because of the fact that the contents of the history becomes obsolete at - /// the end of the session, however, because the \ref hist_InRealm history - /// remains in the Realm file, there are practical complications, and for - /// that reason, such switching shall not be supported. - /// - /// The \ref hist_Sync history type can only be used if the stored history - /// type is also \ref hist_Sync, or when there is no top array - /// yet. Additionally, when the stored history type is \ref hist_Sync, then - /// all subsequent sesssions must have the same type. These restrictions - /// apply because such a history needs to be maintained persistently across - /// sessions. That is, the contents of such a history is not obsolete at the - /// end of the session, and is in general needed during subsequent sessions. + /// switch to \ref hist_None would be possible because of the transient + /// nature of it, however, because the \ref hist_InRealm history remains in + /// the Realm file, there are practical complications, and for that reason, + /// such switching shall not be supported. + /// + /// The \ref hist_SyncClient history type can only be used if the stored + /// history type is also \ref hist_SyncClient, or when there is no top array + /// yet. Likewise, the \ref hist_SyncServer history type can only be used if + /// the stored history type is also \ref hist_SyncServer, or when there is + /// no top array yet. Additionally, when the stored history type is \ref + /// hist_SyncClient or \ref hist_SyncServer, then all subsequent sessions + /// must have the same type. These restrictions apply because such a history + /// needs to be maintained persistently across sessions. /// /// In general, if there is no stored history type (no top array) at the /// beginning of a new session, or if the stored type disagrees with what is /// returned by get_history_type() (which is possible due to particular /// allowed changes of history type), the actual history type (as returned /// by get_history_type()) used during that session, must be stored in the - /// Realm during the first successfully committed transaction of that - /// session, if any are sucessfully committed. But note that there is still - /// no need to expand the top array to store the history type \ref - /// hist_None, due to the rule mentioned above. - /// - /// Due to the rules listed above, a new history type only actually needs to - /// be stored when the history type of the session (get_history_type()) is - /// neither \ref hist_None nor \ref hist_OutOfRealm, and only when that - /// differs from the stored history type, or if there is no top array at the - /// beginning of the session. - /// - /// Summary of session-to-session history type change constraints: - /// - /// If there is no top array at the beginning of a new session, then all - /// history types (as returned by get_history_type()) are possible during - /// that session. Otherwise there must have been a preceding session (at - /// least one that adds the top array), and the following rules then apply: - /// - ///
-    ///
-    ///                      Type stored in
-    ///   Type used during   Realm file at
-    ///   preceding          beginning of     Possible history types (as returned by
-    ///   session            new session      get_history_type()) during new session
-    ///   ----------------------------------------------------------------------------
-    ///   hist_None          hist_None        hist_None, hist_OutOfRealm, hist_InRealm
-    ///   hist_OutOfRealm    hist_None        hist_None, hist_OutOfRealm, hist_InRealm
-    ///   hist_InRealm       hist_InRealm     hist_InRealm
-    ///   hist_Sync          hist_Sync        hist_Sync
-    ///
-    /// 
+ /// Realm during the first successfully committed transaction in that + /// session. But note that there is still no need to expand the top array to + /// store the history type \ref hist_None, due to the rule mentioned above. /// /// This function must return \ref hist_None when, and only when /// get_history() returns null. virtual HistoryType get_history_type() const noexcept = 0; + /// Returns the schema version of the history maintained by this Replication + /// implementation, or 0 if no history is maintained by it. All session + /// participants must agree on history schema version. + /// + /// Must return 0 if get_history_type() returns \ref hist_None. + virtual int get_history_schema_version() const noexcept = 0; + + /// Implementation may assume that this function is only ever called with a + /// stored schema version that is less than what was returned by + /// get_history_schema_version(). + virtual bool is_upgradable_history_schema(int stored_schema_version) const noexcept = 0; + + /// The implementation may assume that this function is only ever called if + /// is_upgradable_history_schema() was called with the same stored schema + /// version, and returned true. This implies that the specified stored + /// schema version is always strictly less than what was returned by + /// get_history_schema_version(). + virtual void upgrade_history_schema(int stored_schema_version) = 0; + /// Returns an object that gives access to the history of changesets in a /// way that allows for continuous transactions to work /// (Group::advance_transact() in particular). diff --git a/Pods/Realm/include/core/realm/row.hpp b/Pods/Realm/include/core/realm/row.hpp index 6261e51..c1439b7 100644 --- a/Pods/Realm/include/core/realm/row.hpp +++ b/Pods/Realm/include/core/realm/row.hpp @@ -91,6 +91,7 @@ class RowFuncs { size_t get_link_count(size_t col_ndx) const noexcept; Mixed get_mixed(size_t col_ndx) const noexcept; DataType get_mixed_type(size_t col_ndx) const noexcept; + template U get(size_t col_ndx) const noexcept; @@ -113,6 +114,12 @@ class RowFuncs { void set_null(size_t col_ndx); void set_null_unique(size_t col_ndx); + template + void set(size_t col_ndx, U&& value, bool is_default = false); + + template + void set_unique(size_t col_ndx, U&& value); + void insert_substring(size_t col_ndx, size_t pos, StringData); void remove_substring(size_t col_ndx, size_t pos, size_t size); @@ -122,6 +129,7 @@ class RowFuncs { void move_last_over(); //@} + size_t get_backlink_count() const noexcept; size_t get_backlink_count(const Table& src_table, size_t src_col_ndx) const noexcept; size_t get_backlink(const Table& src_table, size_t src_col_ndx, size_t backlink_ndx) const noexcept; @@ -186,14 +194,17 @@ class RowFuncs { template class BasicRowExpr : public RowFuncs> { public: - BasicRowExpr() noexcept; + BasicRowExpr() noexcept = default; template BasicRowExpr(const BasicRowExpr&) noexcept; + template + BasicRowExpr(const BasicRow&) noexcept; + private: - T* m_table; // nullptr if detached. - size_t m_row_ndx; // Undefined if detached. + T* m_table = nullptr; // nullptr if detached. + size_t m_row_ndx = 0; // Undefined if detached. BasicRowExpr(T*, size_t init_row_ndx) noexcept; @@ -205,12 +216,12 @@ class BasicRowExpr : public RowFuncs> { // from RowFuncs. friend class RowFuncs>; - // Make m_table and m_col_ndx accessible from BasicRowExpr(const + // Make m_table and m_row_ndx accessible from BasicRowExpr(const // BasicRowExpr&) for any U. template friend class BasicRowExpr; - // Make m_table and m_col_ndx accessible from + // Make m_table and m_row_ndx accessible from // BasicRow::BaicRow(BasicRowExpr) for any U. template friend class BasicRow; @@ -313,11 +324,16 @@ class BasicRow : private RowBase, public RowFuncs> { // from RowFuncs. friend class RowFuncs>; - // Make m_table and m_col_ndx accessible from BasicRow(const BasicRow&) + // Make m_table and m_row_ndx accessible from BasicRow(const BasicRow&) // for any U. template friend class BasicRow; + // Make m_table and m_row_ndx accessible from BasicRowExpr(const + // BasicRow&) for any U. + template + friend class BasicRowExpr; + public: std::unique_ptr> clone_for_handover(std::unique_ptr& patch) const { @@ -592,6 +608,20 @@ inline void RowFuncs::set_null_unique(size_t col_ndx) table()->set_null_unique(col_ndx, row_ndx()); // Throws } +template +template +inline void RowFuncs::set(size_t col_ndx, U&& value, bool is_default) +{ + table()->set(col_ndx, row_ndx(), std::forward(value), is_default); // Throws +} + +template +template +inline void RowFuncs::set_unique(size_t col_ndx, U&& value) +{ + table()->set_unique(col_ndx, row_ndx(), std::forward(value)); // Throws +} + template inline void RowFuncs::insert_substring(size_t col_ndx, size_t pos, StringData value) { @@ -616,6 +646,12 @@ inline void RowFuncs::move_last_over() table()->move_last_over(row_ndx()); // Throws } +template +inline size_t RowFuncs::get_backlink_count() const noexcept +{ + return table()->get_backlink_count(row_ndx()); +} + template inline size_t RowFuncs::get_backlink_count(const Table& src_table, size_t src_col_ndx) const noexcept { @@ -709,17 +745,18 @@ inline size_t RowFuncs::row_ndx() const noexcept template -inline BasicRowExpr::BasicRowExpr() noexcept - : m_table(0) - , m_row_ndx(0) +template +inline BasicRowExpr::BasicRowExpr(const BasicRowExpr& expr) noexcept + : m_table(expr.m_table) + , m_row_ndx(expr.m_row_ndx) { } template template -inline BasicRowExpr::BasicRowExpr(const BasicRowExpr& expr) noexcept - : m_table(expr.m_table) - , m_row_ndx(expr.m_row_ndx) +inline BasicRowExpr::BasicRowExpr(const BasicRow& row) noexcept + : m_table(row.m_table.get()) + , m_row_ndx(row.m_row_ndx) { } diff --git a/Pods/Realm/include/core/realm/spec.hpp b/Pods/Realm/include/core/realm/spec.hpp index 78448c9..6b793f0 100644 --- a/Pods/Realm/include/core/realm/spec.hpp +++ b/Pods/Realm/include/core/realm/spec.hpp @@ -29,12 +29,9 @@ namespace realm { class Table; -class SubspecRef; -class ConstSubspecRef; class Spec { public: - Spec(SubspecRef) noexcept; ~Spec() noexcept; Allocator& get_alloc() const noexcept; @@ -43,7 +40,6 @@ class Spec { void insert_column(size_t column_ndx, ColumnType type, StringData name, ColumnAttr attr = col_attr_None); void rename_column(size_t column_ndx, StringData new_name); - void move_column(size_t from, size_t to); /// Erase the column at the specified index, and move columns at /// succeeding indexes to the next lower index. @@ -59,8 +55,7 @@ class Spec { // reference, it is the responsibility of the application that the // parent Spec object (this) is kept alive for at least as long as // the new Spec object. - SubspecRef get_subtable_spec(size_t column_ndx) noexcept; - ConstSubspecRef get_subtable_spec(size_t column_ndx) const noexcept; + Spec* get_subtable_spec(size_t column_ndx) noexcept; //@} // Column info @@ -78,8 +73,8 @@ class Spec { size_t get_subspec_ndx(size_t column_ndx) const noexcept; ref_type get_subspec_ref(size_t subspec_ndx) const noexcept; - SubspecRef get_subspec_by_ndx(size_t subspec_ndx) noexcept; - ConstSubspecRef get_subspec_by_ndx(size_t subspec_ndx) const noexcept; + Spec* get_subspec_by_ndx(size_t subspec_ndx) noexcept; + const Spec* get_subspec_by_ndx(size_t subspec_ndx) const noexcept; // Auto Enumerated string columns void upgrade_string_to_enum(size_t column_ndx, ref_type keys_ref, ArrayParent*& keys_parent, size_t& keys_ndx); @@ -90,7 +85,11 @@ class Spec { // Links size_t get_opposite_link_table_ndx(size_t column_ndx) const noexcept; void set_opposite_link_table_ndx(size_t column_ndx, size_t table_ndx); + + // Backlinks bool has_backlinks() const noexcept; + size_t first_backlink_column_index() const noexcept; + size_t backlink_column_count() const noexcept; void set_backlink_origin_column(size_t backlink_col_ndx, size_t origin_col_ndx); size_t get_origin_column_ndx(size_t backlink_col_ndx) const noexcept; size_t find_backlink_column(size_t origin_table_ndx, size_t origin_col_ndx) const noexcept; @@ -107,6 +106,7 @@ class Spec { bool operator!=(const Spec&) const noexcept; //@} + void detach() noexcept; void destroy() noexcept; size_t get_ndx_in_parent() const noexcept; @@ -134,17 +134,28 @@ class Spec { ArrayInteger m_attr; // 3rd slot in m_top Array m_subspecs; // 4th slot in m_top (optional) Array m_enumkeys; // 5th slot in m_top (optional) + struct SubspecPtr { + SubspecPtr(bool is_spec_ptr = false) + : m_is_spec_ptr(is_spec_ptr) + { + } + std::unique_ptr m_spec; + bool m_is_spec_ptr; + }; + using SubspecPtrs = std::vector; + SubspecPtrs m_subspec_ptrs; bool m_has_strong_link_columns; Spec(Allocator&) noexcept; // Unattached - void init(ref_type) noexcept; + bool init(ref_type) noexcept; void init(MemRef) noexcept; - void init(SubspecRef) noexcept; void update_has_strong_link_columns() noexcept; + void reset_subspec_ptrs(); + void adj_subspec_ptrs(); - // Similar in function to Array::init_from_parent(). - void init_from_parent() noexcept; + // Returns true in case the ref has changed. + bool init_from_parent() noexcept; ref_type get_ref() const noexcept; @@ -153,7 +164,7 @@ class Spec { /// note that this works only for non-transactional commits. Table /// accessors obtained during a transaction are always detached /// when the transaction ends. - void update_from_parent(size_t old_baseline) noexcept; + bool update_from_parent(size_t old_baseline) noexcept; void set_parent(ArrayParent*, size_t ndx_in_parent) noexcept; @@ -184,52 +195,6 @@ class Spec { friend class Table; }; - -class SubspecRef { -public: - struct const_cast_tag { - }; - SubspecRef(const_cast_tag, ConstSubspecRef r) noexcept; - ~SubspecRef() noexcept - { - } - Allocator& get_alloc() const noexcept - { - return m_parent->get_alloc(); - } - -private: - Array* const m_parent; - size_t const m_ndx_in_parent; - - SubspecRef(Array* parent, size_t ndx_in_parent) noexcept; - - friend class Spec; - friend class ConstSubspecRef; -}; - -class ConstSubspecRef { -public: - ConstSubspecRef(SubspecRef r) noexcept; - ~ConstSubspecRef() noexcept - { - } - Allocator& get_alloc() const noexcept - { - return m_parent->get_alloc(); - } - -private: - const Array* const m_parent; - size_t const m_ndx_in_parent; - - ConstSubspecRef(const Array* parent, size_t ndx_in_parent) noexcept; - - friend class Spec; - friend class SubspecRef; -}; - - // Implementation: inline Allocator& Spec::get_alloc() const noexcept @@ -251,17 +216,6 @@ inline ref_type Spec::get_subspec_ref(size_t subspec_ndx) const noexcept return m_subspecs.get_as_ref(subspec_ndx); } -inline Spec::Spec(SubspecRef r) noexcept - : m_top(r.m_parent->get_alloc()) - , m_types(r.m_parent->get_alloc()) - , m_names(r.m_parent->get_alloc()) - , m_attr(r.m_parent->get_alloc()) - , m_subspecs(r.m_parent->get_alloc()) - , m_enumkeys(r.m_parent->get_alloc()) -{ - init(r); -} - // Uninitialized Spec (call init() to init) inline Spec::Spec(Allocator& alloc) noexcept : m_top(alloc) @@ -273,49 +227,23 @@ inline Spec::Spec(Allocator& alloc) noexcept { } -inline SubspecRef Spec::get_subtable_spec(size_t column_ndx) noexcept +inline Spec* Spec::get_subtable_spec(size_t column_ndx) noexcept { REALM_ASSERT(column_ndx < get_column_count()); REALM_ASSERT(get_column_type(column_ndx) == col_type_Table); size_t subspec_ndx = get_subspec_ndx(column_ndx); - return SubspecRef(&m_subspecs, subspec_ndx); -} - -inline ConstSubspecRef Spec::get_subtable_spec(size_t column_ndx) const noexcept -{ - REALM_ASSERT(column_ndx < get_column_count()); - REALM_ASSERT(get_column_type(column_ndx) == col_type_Table); - size_t subspec_ndx = get_subspec_ndx(column_ndx); - return ConstSubspecRef(&m_subspecs, subspec_ndx); -} - -inline SubspecRef Spec::get_subspec_by_ndx(size_t subspec_ndx) noexcept -{ - return SubspecRef(&m_subspecs, subspec_ndx); + return get_subspec_by_ndx(subspec_ndx); } -inline ConstSubspecRef Spec::get_subspec_by_ndx(size_t subspec_ndx) const noexcept +inline const Spec* Spec::get_subspec_by_ndx(size_t subspec_ndx) const noexcept { return const_cast(this)->get_subspec_by_ndx(subspec_ndx); } -inline void Spec::init(ref_type ref) noexcept -{ - MemRef mem(ref, get_alloc()); - init(mem); -} - -inline void Spec::init(SubspecRef r) noexcept -{ - m_top.set_parent(r.m_parent, r.m_ndx_in_parent); - ref_type ref = r.m_parent->get_as_ref(r.m_ndx_in_parent); - init(ref); -} - -inline void Spec::init_from_parent() noexcept +inline bool Spec::init_from_parent() noexcept { ref_type ref = m_top.get_ref_from_parent(); - init(ref); + return init(ref); } inline void Spec::destroy() noexcept @@ -431,6 +359,16 @@ inline bool Spec::has_backlinks() const noexcept // the user. } +inline size_t Spec::first_backlink_column_index() const noexcept +{ + return m_names.size(); +} + +inline size_t Spec::backlink_column_count() const noexcept +{ + return m_types.size() - m_names.size(); +} + // Spec will have a subspec when it contains a column which is one of: // link, linklist, backlink, or subtable. It is possible for m_top.size() // to contain an entry for m_subspecs (at index 3) but this reference @@ -445,32 +383,6 @@ inline bool Spec::operator!=(const Spec& s) const noexcept return !(*this == s); } - -inline SubspecRef::SubspecRef(Array* parent, size_t ndx_in_parent) noexcept - : m_parent(parent) - , m_ndx_in_parent(ndx_in_parent) -{ -} - -inline SubspecRef::SubspecRef(const_cast_tag, ConstSubspecRef r) noexcept - : m_parent(const_cast(r.m_parent)) - , m_ndx_in_parent(r.m_ndx_in_parent) -{ -} - -inline ConstSubspecRef::ConstSubspecRef(const Array* parent, size_t ndx_in_parent) noexcept - : m_parent(parent) - , m_ndx_in_parent(ndx_in_parent) -{ -} - -inline ConstSubspecRef::ConstSubspecRef(SubspecRef r) noexcept - : m_parent(r.m_parent) - , m_ndx_in_parent(r.m_ndx_in_parent) -{ -} - - } // namespace realm #endif // REALM_SPEC_HPP diff --git a/Pods/Realm/include/core/realm/string_data.hpp b/Pods/Realm/include/core/realm/string_data.hpp index 932c1ef..a617ebe 100644 --- a/Pods/Realm/include/core/realm/string_data.hpp +++ b/Pods/Realm/include/core/realm/string_data.hpp @@ -19,22 +19,18 @@ #ifndef REALM_STRING_HPP #define REALM_STRING_HPP -#include +#include +#include +#include + #include -#include -#include -#include #include -#include - #include #include - -#include -#include -#include -#include -#include +#include +#include +#include +#include namespace realm { @@ -92,7 +88,7 @@ class StringData { // StringData does not store data, callers must manage their own strings. template - StringData(std::basic_string&&) = delete; + StringData(const std::basic_string&&) = delete; template StringData(const util::Optional>&); @@ -165,6 +161,11 @@ class StringData { size_t m_size; static bool matchlike(const StringData& text, const StringData& pattern) noexcept; + static bool matchlike_ins(const StringData& text, const StringData& pattern_upper, + const StringData& pattern_lower) noexcept; + + friend bool string_like_ins(StringData, StringData) noexcept; + friend bool string_like_ins(StringData, StringData, StringData) noexcept; }; @@ -331,74 +332,6 @@ inline bool StringData::contains(StringData d, const std::array &c return false; } -inline bool StringData::matchlike(const StringData& text, const StringData& pattern) noexcept -{ - std::vector textpos; - std::vector patternpos; - size_t p1 = 0; // position in text (haystack) - size_t p2 = 0; // position in pattern (needle) - - while (true) { - if (p1 == text.size()) { - if (p2 == pattern.size()) - return true; - if (p2 == pattern.size() - 1 && pattern[p2] == '*') - return true; - goto no_match; - } - if (p2 == pattern.size()) - goto no_match; - - if (pattern[p2] == '*') { - textpos.push_back(p1); - patternpos.push_back(++p2); - continue; - } - if (pattern[p2] == '?') { - // utf-8 encoded characters may take up multiple bytes - if ((text[p1] & 0x80) == 0) { - ++p1; - ++p2; - continue; - } - else { - size_t p = 1; - while (p1 + p != text.size() && (text[p1 + p] & 0xc0) == 0x80) - ++p; - p1 += p; - ++p2; - continue; - } - } - - if (pattern[p2] == text[p1]) { - ++p1; - ++p2; - continue; - } - - no_match: - if (textpos.empty()) - return false; - else { - if (p1 == text.size()) { - textpos.pop_back(); - patternpos.pop_back(); - - if (textpos.empty()) - return false; - - p1 = textpos.back(); - } - else { - p1 = textpos.back(); - textpos.back() = ++p1; - } - p2 = patternpos.back(); - } - } -} - inline bool StringData::like(StringData d) const noexcept { if (is_null() || d.is_null()) { diff --git a/Pods/Realm/include/core/realm/sync/changeset.hpp b/Pods/Realm/include/core/realm/sync/changeset.hpp new file mode 100644 index 0000000..a73644b --- /dev/null +++ b/Pods/Realm/include/core/realm/sync/changeset.hpp @@ -0,0 +1,651 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2017] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ + +#ifndef REALM_SYNC_CHANGESET_HPP +#define REALM_SYNC_CHANGESET_HPP + +#include +#include + +#include + +namespace realm { +namespace sync { + +using InternStrings = std::vector; + +struct BadChangesetError : std::exception { + const char* message; + BadChangesetError() : BadChangesetError("Bad changeset") {} + BadChangesetError(const char* msg) : message(msg) {} + const char* what() const noexcept override + { + return message; + } +}; + +struct Changeset { + struct Range; + using timestamp_type = uint_fast64_t; + using file_ident_type = uint_fast64_t; + using version_type = uint_fast64_t; // FIXME: Get from `History`. + + Changeset(); + struct share_buffers_tag {}; + Changeset(const Changeset&, share_buffers_tag); + Changeset(Changeset&&) = default; + Changeset& operator=(Changeset&&) = default; + Changeset(const Changeset&) = delete; + Changeset& operator=(const Changeset&) = delete; + + InternString intern_string(StringData); // Slow! + StringData string_data() const noexcept; + + util::StringBuffer& string_buffer() noexcept; + const util::StringBuffer& string_buffer() const noexcept; + const InternStrings& interned_strings() const noexcept; + InternStrings& interned_strings() noexcept; + + StringBufferRange get_intern_string(InternString) const noexcept; + util::Optional try_get_intern_string(InternString) const noexcept; + util::Optional try_get_string(StringBufferRange) const noexcept; + StringData get_string(StringBufferRange) const noexcept; + StringData get_string(InternString) const noexcept; + StringBufferRange append_string(StringData); + + // Interface to imitate std::vector: + template struct IteratorImpl; + using iterator = IteratorImpl; + using const_iterator = IteratorImpl; + using value_type = Instruction; + iterator begin() noexcept; + iterator end() noexcept; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + bool empty() const noexcept; + + /// Size of the Changeset, not counting tombstones. + /// + /// FIXME: This is an O(n) operation. + size_t size() const noexcept; + + void clear() noexcept; + + //@{ + /// Insert instructions, invalidating all iterators. + iterator insert(const_iterator pos, Instruction); + template + iterator insert(const_iterator pos, InputIt begin, InputIt end); + //@} + + /// Erase an instruction, invalidating all iterators. + iterator erase(const_iterator); + + /// Insert an instruction at the end, invalidating all iterators. + void push_back(const Instruction&); + + //@{ + /// Insert instructions at \a position without invalidating other + /// iterators. + /// + /// Only iterators created before any call to `insert_stable()` may be + /// considered stable across calls to `insert_stable()`. In addition, + /// "iterator stability" has a very specific meaning here: Other copies of + /// \a position in the program will point to the newly inserted elements + /// after calling `insert_stable()`, rather than point to the value at the + /// position prior to insertion. This is different from, say, a tree + /// structure, where iterator stability signifies the property that + /// iterators keep pointing to the same element after insertion before or + /// after that position. + /// + /// For the purpose of supporting `ChangesetIndex`, and the OT merge + /// algorithm, these semantics are acceptable, since prepended instructions + /// can never create new object or table references. + iterator insert_stable(const_iterator position, Instruction); + template + iterator insert_stable(const_iterator position, InputIt begin, InputIt end); + //@} + + /// Erase instruction at \a position without invalidating other iterators. + /// If erasing the object would invalidate other iterators, it is turned + /// into a tombstone instead, and subsequent derefencing of the iterator + /// will return `nullptr`. An iterator pointing to a tombstone remains valid + /// and can be incremented. + /// + /// Only iterators created before any call to `insert_stable()` may be + /// considered stable across calls to `erase_stable()`. If other copies of + /// \a position exist in the program, they will either point to the + /// subsequent element if that element was previously inserted with + /// `insert_stable()`, or otherwise it will be turned into a tombstone. + iterator erase_stable(const_iterator position); + +#if REALM_DEBUG + struct Reflector; + struct Printer; + void verify() const; + void print(std::ostream&) const; + void print() const; // prints to std::err +#endif + + /// The version that this changeset produced. Note: This may not be the + /// version produced by this changeset on the client on which this changeset + /// originated, but may for instance be the version produced on the server + /// after receiving and re-sending this changeset to another client. + version_type version = 0; + + /// On clients, the last integrated server version. On the server, this is + /// the last integrated client version. + version_type last_integrated_remote_version = 0; + + /// Timestamp at origin when the original untransformed changeset was + /// produced. + timestamp_type origin_timestamp = 0; + + /// The identifier of the file in the context of which the original + /// untransformed changeset was produced. + file_ident_type origin_file_ident = 0; + +private: + struct MultiInstruction { + std::vector instructions; + }; + static_assert(sizeof(MultiInstruction) <= Instruction::max_instruction_size, "Instruction::max_instruction_size too low"); + + // In order to achieve iterator semi-stability (just enough to be able to + // run the merge algorithm while maintaining a ChangesetIndex), a Changeset + // is really a list of lists. A Changeset is a vector of + // `InstructionContainer`s, and each `InstructionContainer` represents 0-N + // "real" instructions. + // + // As an optimization, there is a special case for when the + // `InstructionContainer` represents exactly 1 instruction, in which case it + // is represented inside the `InstructionContainer` without any additional + // allocations or indirections. The `InstructionContainer` derived from + // the `Instruction` struct, and co-opts the `type` field such that if the + // (invalid) value of `type` is 0xff, the contents of the `Instruction` are + // instead interpreted as an instance of `MultiInstruction`, which holds + // a vector of `Instruction`s. + // + // The size of the `MultiInstruction` may also be zero, in which case it is + // considered a "tombstone" - always as a result of a call to + // `Changeset::erase_stable()`. The potential existence of these tombstones + // is the reason that the value type of `Changeset::iterator` is + // `Instruction*`, rather than `Instruction&`. + // + // FIXME: It would be better if `Changeset::iterator::value_type` could be + // `util::Optional`, but this is prevented by a bug in + // `util::Optional`. + struct InstructionContainer : Instruction { + InstructionContainer(); + InstructionContainer(const Instruction& instr); + InstructionContainer(InstructionContainer&&) noexcept; + InstructionContainer(const InstructionContainer&); + ~InstructionContainer(); + InstructionContainer& operator=(InstructionContainer&&) noexcept; + InstructionContainer& operator=(const InstructionContainer&); + + bool is_multi() const noexcept; + void convert_to_multi(); + void insert(size_t position, Instruction instr); + void erase(size_t position); + size_t size() const noexcept; + + Instruction& at(size_t pos) noexcept; + const Instruction& at(size_t pos) const noexcept; + + MultiInstruction& get_multi() noexcept; + const MultiInstruction& get_multi() const noexcept; + }; + + std::vector m_instructions; + std::shared_ptr m_string_buffer; + std::shared_ptr m_strings; + + iterator const_iterator_to_iterator(const_iterator); +}; + +/// An iterator type that hides the implementation details of the support for +/// iterator stability. +/// +/// A `Changeset::iterator` is composed of an +/// `std::vector::iterator` and a `size_t` representing +/// the index into the current `InstructionContainer`. If that container is +/// empty, and the position is zero, the iterator is pointing to a tombstone. +template +struct Changeset::IteratorImpl { + using list_type = std::vector; + using inner_iterator_type = std::conditional_t; + + // reference_type is a pointer because we have no way to create a reference + // to a tombstone instruction. Alternatively, it could have been + // `util::Optional`, but that runs into other issues. + using reference_type = std::conditional_t; + + using pointer_type = std::conditional_t; + using difference_type = std::ptrdiff_t; + + IteratorImpl() : m_pos(0) {} + IteratorImpl(const IteratorImpl& other) : m_inner(other.m_inner), m_pos(other.m_pos) {} + template + IteratorImpl(const IteratorImpl& other, std::enable_if_t* = nullptr) + : m_inner(other.m_inner), m_pos(other.m_pos) {} + IteratorImpl(inner_iterator_type inner, size_t pos = 0) : m_inner(inner), m_pos(pos) {} + + IteratorImpl& operator++() + { + ++m_pos; + if (m_pos >= m_inner->size()) { + ++m_inner; + m_pos = 0; + } + return *this; + } + + IteratorImpl operator++(int) + { + auto copy = *this; + ++(*this); + return copy; + } + + IteratorImpl& operator--() + { + if (m_pos == 0) { + --m_inner; + m_pos = m_inner->size(); + if (m_pos != 0) + --m_pos; + } + else { + --m_pos; + } + return *this; + } + + IteratorImpl operator--(int) + { + auto copy = *this; + --(*this); + return copy; + } + + reference_type operator*() const + { + if (m_inner->size()) { + return &m_inner->at(m_pos); + } + // It was a tombstone. + return nullptr; + } + + pointer_type operator->() const + { + if (m_inner->size()) { + return &m_inner->at(m_pos); + } + // It was a tombstone. + return nullptr; + } + + bool operator==(const IteratorImpl& other) const + { + return m_inner == other.m_inner && m_pos == other.m_pos; + } + + bool operator!=(const IteratorImpl& other) const + { + return !(*this == other); + } + + bool operator<(const IteratorImpl& other) const + { + if (m_inner == other.m_inner) + return m_pos < other.m_pos; + return m_inner < other.m_inner; + } + + bool operator<=(const IteratorImpl& other) const + { + if (m_inner == other.m_inner) + return m_pos <= other.m_pos; + return m_inner < other.m_inner; + } + + bool operator>(const IteratorImpl& other) const + { + if (m_inner == other.m_inner) + return m_pos > other.m_pos; + return m_inner > other.m_inner; + } + + bool operator>=(const IteratorImpl& other) const + { + if (m_inner == other.m_inner) + return m_pos >= other.m_pos; + return m_inner > other.m_inner; + } + + inner_iterator_type m_inner; + size_t m_pos; +}; + +struct Changeset::Range { + iterator begin; + iterator end; +}; + +#if REALM_DEBUG +struct Changeset::Reflector { + struct Tracer { + virtual void name(StringData) = 0; + virtual void field(StringData, StringData) = 0; + virtual void field(StringData, ObjectID) = 0; + virtual void field(StringData, int64_t) = 0; + virtual void field(StringData, double) = 0; + virtual void after_each() {} + virtual void before_each() {} + }; + + Reflector(Tracer& tracer, const Changeset& log) : + m_tracer(tracer), m_log(log) + {} + + void visit_all() const; +private: + Tracer& m_tracer; + const Changeset& m_log; + + friend struct Instruction; // permit access for visit() +#define REALM_DEFINE_REFLECTOR_VISITOR(X) void operator()(const Instruction::X&) const; + REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DEFINE_REFLECTOR_VISITOR) +#undef REALM_DEFINE_REFLECTOR_VISITOR +}; + +struct Changeset::Printer : Changeset::Reflector::Tracer { + explicit Printer(std::ostream& os) : m_out(os) + {} + + // ChangesetReflector::Tracer interface: + void name(StringData) final; + void field(StringData, StringData) final; + void field(StringData, ObjectID) final; + void field(StringData, int64_t) final; + void field(StringData, double) final; + void after_each() final; + +private: + std::ostream& m_out; + void pad_or_ellipsis(StringData, int width) const; +}; +#endif // REALM_DEBUG + + + +/// Implementation: + +inline Changeset::iterator Changeset::begin() noexcept +{ + return m_instructions.begin(); +} + +inline Changeset::iterator Changeset::end() noexcept +{ + return m_instructions.end(); +} + +inline Changeset::const_iterator Changeset::begin() const noexcept +{ + return m_instructions.begin(); +} + +inline Changeset::const_iterator Changeset::end() const noexcept +{ + return m_instructions.end(); +} + +inline Changeset::const_iterator Changeset::cbegin() const noexcept +{ + return m_instructions.cbegin(); +} + +inline Changeset::const_iterator Changeset::cend() const noexcept +{ + return m_instructions.end(); +} + +inline bool Changeset::empty() const noexcept +{ + return size() == 0; +} + +inline size_t Changeset::size() const noexcept +{ + size_t sum = 0; + for (auto& x: m_instructions) + sum += x.size(); + return sum; +} + +inline void Changeset::clear() noexcept +{ + m_instructions.clear(); +} + +inline util::Optional Changeset::try_get_intern_string(InternString string) const noexcept +{ + if (string.value >= m_strings->size()) + return util::none; + return (*m_strings)[string.value]; +} + +inline StringBufferRange Changeset::get_intern_string(InternString string) const noexcept +{ + auto str = try_get_intern_string(string); + REALM_ASSERT(str); + return *str; +} + +inline InternStrings& Changeset::interned_strings() noexcept +{ + return *m_strings; +} + +inline const InternStrings& Changeset::interned_strings() const noexcept +{ + return *m_strings; +} + +inline util::StringBuffer& Changeset::string_buffer() noexcept +{ + return *m_string_buffer; +} + +inline const util::StringBuffer& Changeset::string_buffer() const noexcept +{ + return *m_string_buffer; +} + +inline util::Optional Changeset::try_get_string(StringBufferRange range) const noexcept +{ + if (range.offset > m_string_buffer->size()) + return util::none; + if (range.offset + range.size > m_string_buffer->size()) + return util::none; + return StringData{m_string_buffer->data() + range.offset, range.size}; +} + +inline StringData Changeset::get_string(StringBufferRange range) const noexcept +{ + auto string = try_get_string(range); + REALM_ASSERT(string); + return *string; +} + +inline StringData Changeset::get_string(InternString string) const noexcept +{ + return get_string(get_intern_string(string)); +} + +inline StringData Changeset::string_data() const noexcept +{ + return StringData{m_string_buffer->data(), m_string_buffer->size()}; +} + +inline StringBufferRange Changeset::append_string(StringData string) +{ + m_string_buffer->reserve(1024); // we expect more strings + size_t offset = m_string_buffer->size(); + m_string_buffer->append(string.data(), string.size()); + return StringBufferRange{uint32_t(offset), uint32_t(string.size())}; +} + +inline Changeset::iterator Changeset::insert(const_iterator pos, Instruction instr) +{ + Instruction* p = &instr; + return insert(pos, p, p + 1); +} + +template +inline Changeset::iterator Changeset::insert(const_iterator pos, InputIt begin, InputIt end) +{ + if (pos.m_pos == 0) + return m_instructions.insert(pos.m_inner, begin, end); + return insert_stable(pos, begin, end); +} + +inline Changeset::iterator Changeset::erase(const_iterator pos) +{ + if (pos.m_inner->size() <= 1) + return m_instructions.erase(pos.m_inner); + return erase_stable(pos); +} + +inline Changeset::iterator Changeset::insert_stable(const_iterator pos, Instruction instr) +{ + Instruction* p = &instr; + return insert_stable(pos, p, p + 1); +} + +template +inline Changeset::iterator Changeset::insert_stable(const_iterator cpos, InputIt begin, InputIt end) +{ + iterator pos = const_iterator_to_iterator(cpos); + size_t i = 0; + for (auto it = begin; it != end; ++it, ++i) { + pos.m_inner->insert(pos.m_pos + i, *it); + } + return pos; +} + +inline Changeset::iterator Changeset::erase_stable(const_iterator cpos) +{ + auto pos = const_iterator_to_iterator(cpos); + auto begin = m_instructions.begin(); + auto end = m_instructions.end(); + REALM_ASSERT(pos.m_inner >= begin); + REALM_ASSERT(pos.m_inner < end); + pos.m_inner->erase(pos.m_pos); + if (pos.m_pos >= pos.m_inner->size()) { + do { + ++pos.m_inner; + } while (pos.m_inner != end && pos.m_inner->size() == 0); + pos.m_pos = 0; + } + return pos; +} + +inline void Changeset::push_back(const Instruction& instr) +{ + m_instructions.emplace_back(instr); +} + +inline auto Changeset::const_iterator_to_iterator(const_iterator cpos) -> iterator +{ + size_t offset = cpos.m_inner - m_instructions.cbegin(); + return iterator{m_instructions.begin() + offset, cpos.m_pos}; +} + +inline Changeset::InstructionContainer::~InstructionContainer() +{ + if (is_multi()) { + get_multi().~MultiInstruction(); + } + // Instruction subtypes are required to be POD-types (trivially + // destructible), and this is checked by a static_assert in + // instructions.hpp. Therefore, it is safe to do nothing if this is not a + // multi-instruction. +} + +inline bool Changeset::InstructionContainer::is_multi() const noexcept +{ + return type == Type(InstrTypeMultiInstruction); +} + +inline size_t Changeset::InstructionContainer::size() const noexcept +{ + if (is_multi()) + return get_multi().instructions.size(); + return 1; +} + +inline Instruction& Changeset::InstructionContainer::at(size_t pos) noexcept +{ + REALM_ASSERT(pos < size()); + if (is_multi()) + return get_multi().instructions[pos]; + return *this; +} + +inline const Instruction& Changeset::InstructionContainer::at(size_t pos) const noexcept +{ + REALM_ASSERT(pos < size()); + if (is_multi()) + return get_multi().instructions[pos]; + return *this; +} + +inline Changeset::MultiInstruction& Changeset::InstructionContainer::get_multi() noexcept +{ + REALM_ASSERT(is_multi()); + return *reinterpret_cast(&m_storage); +} + +inline const Changeset::MultiInstruction& Changeset::InstructionContainer::get_multi() const noexcept +{ + REALM_ASSERT(is_multi()); + return *reinterpret_cast(&m_storage); +} + +} // namespace sync +} // namespace realm + +namespace std { + +template +struct iterator_traits> { + using difference_type = std::ptrdiff_t; + using iterator_category = std::bidirectional_iterator_tag; +}; + +} // namespace std + +#endif // REALM_SYNC_CHANGESET_HPP diff --git a/Pods/Realm/include/core/realm/sync/changeset_cooker.hpp b/Pods/Realm/include/core/realm/sync/changeset_cooker.hpp index 2830473..3d003be 100644 --- a/Pods/Realm/include/core/realm/sync/changeset_cooker.hpp +++ b/Pods/Realm/include/core/realm/sync/changeset_cooker.hpp @@ -27,7 +27,7 @@ namespace realm { namespace sync { /// Copy raw changesets unmodified. -class TrivialChangesetCooker: public SyncHistory::ChangesetCooker { +class TrivialChangesetCooker: public ClientHistory::ChangesetCooker { public: bool cook_changeset(const Group&, const char* changeset, std::size_t changeset_size, diff --git a/Pods/Realm/include/core/realm/sync/changeset_encoder.hpp b/Pods/Realm/include/core/realm/sync/changeset_encoder.hpp new file mode 100644 index 0000000..d443862 --- /dev/null +++ b/Pods/Realm/include/core/realm/sync/changeset_encoder.hpp @@ -0,0 +1,109 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2017] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ + +#ifndef REALM_SYNC_CHANGESET_ENCODER_HPP +#define REALM_SYNC_CHANGESET_ENCODER_HPP + +#include + +namespace realm { +namespace sync { + +struct ChangesetEncoder: InstructionHandler { + util::AppendBuffer release() noexcept; + void reset() noexcept; + const util::AppendBuffer& buffer() const noexcept; + InternString intern_string(StringData); + + void set_intern_string(uint32_t index, StringBufferRange) override; + // FIXME: This doesn't copy the input, but the drawback is that there can + // only be a single StringBufferRange per instruction. Luckily, no + // instructions exist that require two or more. + StringBufferRange add_string_range(StringData) override; + void operator()(const Instruction&) override; + +#define REALM_DEFINE_INSTRUCTION_HANDLER(X) void operator()(const Instruction::X&); + REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DEFINE_INSTRUCTION_HANDLER) +#undef REALM_DEFINE_INSTRUCTION_HANDLER + +protected: + template static void encode(E& encoder, const Instruction&); + + StringData get_string(StringBufferRange) const noexcept; + +private: + template + void append(Instruction::Type t, Args&&...); + void append_string(StringBufferRange); // does not intern the string + void append_bytes(const void*, size_t); + + template void append_int(T); + template char* encode_int(char* buffer, T value); + void append_payload(const Instruction::Payload&); + void append_value(DataType); + void append_value(bool); + void append_value(uint8_t); + void append_value(int64_t); + void append_value(uint32_t); + void append_value(uint64_t); + void append_value(float); + void append_value(double); + void append_value(InternString); + void append_value(sync::ObjectID); + void append_value(Timestamp); + + util::AppendBuffer m_buffer; + std::unordered_map m_intern_strings_rev; + StringData m_string_range; +}; + +void encode_changeset(const Changeset&, util::AppendBuffer& out_buffer); + + + + +// Implementation + +inline const util::AppendBuffer& ChangesetEncoder::buffer() const noexcept +{ + return m_buffer; +} + +inline void ChangesetEncoder::operator()(const Instruction& instr) +{ + encode(*this, instr); // Throws +} + +template inline void ChangesetEncoder::encode(E& encoder, const Instruction& instr) +{ + instr.visit(encoder); // Throws +} + +inline StringData ChangesetEncoder::get_string(StringBufferRange range) const noexcept +{ + const char* data = m_string_range.data() + range.offset; + std::size_t size = std::size_t(range.size); + return StringData{data, size}; +} + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_CHANGESET_ENCODER_HPP diff --git a/Pods/Realm/include/core/realm/sync/changeset_parser.hpp b/Pods/Realm/include/core/realm/sync/changeset_parser.hpp new file mode 100644 index 0000000..f331569 --- /dev/null +++ b/Pods/Realm/include/core/realm/sync/changeset_parser.hpp @@ -0,0 +1,47 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2017] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ + +#ifndef REALM_SYNC_CHANGESET_PARSER_HPP +#define REALM_SYNC_CHANGESET_PARSER_HPP + +#include +#include + +namespace realm { +namespace sync { + +struct ChangesetParser { + /// Throws BadChangesetError if parsing fails. + /// + /// FIXME: Consider using std::error_code instead of throwing exceptions on + /// parse errors. + void parse(_impl::NoCopyInputStream&, InstructionHandler&); +private: + struct State; +}; + +void parse_changeset(_impl::NoCopyInputStream&, Changeset& out_log); +void parse_changeset(_impl::InputStream&, Changeset& out_log); + + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_CHANGESET_PARSER_HPP diff --git a/Pods/Realm/include/core/realm/sync/client.hpp b/Pods/Realm/include/core/realm/sync/client.hpp index b8710ca..8f1326c 100644 --- a/Pods/Realm/include/core/realm/sync/client.hpp +++ b/Pods/Realm/include/core/realm/sync/client.hpp @@ -20,7 +20,8 @@ #ifndef REALM_SYNC_CLIENT_HPP #define REALM_SYNC_CLIENT_HPP -#include +#include +#include #include #include #include @@ -29,7 +30,8 @@ #include #include -#include +#include +#include #include namespace realm { @@ -46,21 +48,32 @@ class Client { /// amount of time (i.e., a server hammering protection mechanism). normal, - /// Delay reconnect attempts indefinitely. For testing purposes only. + /// For testing purposes only. + /// + /// Never reconnect automatically after the connection is closed due to + /// an error. Allow immediate reconnect if the connection was closed + /// voluntarily (e.g., due to sessions being abandoned). /// - /// A reconnect attempt can be manually scheduled by calling - /// cancel_reconnect_delay(). In particular, when a connection breaks, - /// or when an attempt at establishing the connection fails, the error - /// handler is called. If one calls cancel_reconnect_delay() from that - /// invocation of the error handler, the effect is to allow another - /// reconnect attempt to occur. - never, - - /// Never delay reconnect attempts. Perform them immediately. For - /// testing purposes only. - immediate + /// In this mode, Client::cancel_reconnect_delay() and + /// Session::cancel_reconnect_delay() can still be used to trigger + /// another reconnection attempt (with no delay) after an error has + /// caused the connection to be closed. + testing }; + using RoundtripTimeHandler = void(milliseconds_type roundtrip_time); + + // FIXME: The default values for `connect_timeout`, `ping_keepalive_period`, + // and `pong_keepalive_timeout` ought to be much lower (2 minutes, 1 minute, + // and 2 minutes) than they are. Their current values are due to the fact + // that the server is single threaded, and that some operations take more + // than 5 minutes to complete. + static constexpr milliseconds_type default_connect_timeout = 600000; // 10 minutes + static constexpr milliseconds_type default_connection_linger_time = 30000; // 30 seconds + static constexpr milliseconds_type default_ping_keepalive_period = 600000; // 10 minutes + static constexpr milliseconds_type default_pong_keepalive_timeout = 600000; // 10 minutes + static constexpr milliseconds_type default_fast_reconnect_limit = 60000; // 1 minute + struct Config { Config() {} @@ -73,33 +86,10 @@ class Client { /// specified, the client will use an instance of util::StderrLogger /// with the log level threshold set to util::Logger::Level::info. The /// client does not require a thread-safe logger, and it guarantees that - /// all logging happens on behalf of the thread that executes run(). + /// all logging happens either on behalf of the constructor or on behalf + /// of the invocation of run(). util::Logger* logger = nullptr; - /// verify_servers_ssl_certificate controls whether the server certificate - /// is verified for SSL connections. It should always be true in production. - /// - /// A server certificate is verified by first checking that the - /// certificate has a valid signature chain back to a trust/anchor certificate, - /// and secondly checking that the host name of the Realm URL matches - /// a host name contained in the certificate. - /// The host name of the certificate is stored in either Common Name or - /// the Alternative Subject Name (DNS section). - /// - /// From the point of view of OpenSSL, setting verify_servers_ssl_certificate - /// to false means calling `SSL_set_verify()` with `SSL_VERIFY_NONE`. - /// Setting verify_servers_ssl_certificate to true means calling `SSL_set_verify()` - /// with `SSL_VERIFY_PEER`, and setting the host name using the function - /// X509_VERIFY_PARAM_set1_host() (OpenSSL version 1.0.2 or newer). - /// For other platforms, an equivalent procedure is followed. - bool verify_servers_ssl_certificate = true; - - /// ssl_trust_certificate_path is the path of a trust/anchor certificate - /// used by the client to verify the server certificate. - /// If ssl_trust_certificate_path is None (default), the default device - /// trust/anchor store is used. - util::Optional ssl_trust_certificate_path; // default None - /// Use ports 80 and 443 by default instead of 7800 and 7801 /// respectively. Ideally, these default ports should have been made /// available via a different URI scheme instead (http/https or ws/wss). @@ -111,9 +101,9 @@ class Client { /// Create a separate connection for each session. For testing purposes /// only. /// - // FIXME: This setting is ignored for now, due to limitations in the - // load balancer. - bool one_connection_per_session = false; + /// FIXME: This setting needs to be true for now, due to limitations in + /// the load balancer. + bool one_connection_per_session = true; /// Do not access the local file system. Sessions will act as if /// initiated on behalf of an empty (or nonexisting) local Realm @@ -125,8 +115,93 @@ class Client { /// The default changeset cooker to be used by new sessions. Can be /// overridden by Session::Config::changeset_cooker. /// - /// \sa make_sync_history(), TrivialChangesetCooker. - SyncHistory::ChangesetCooker* changeset_cooker = nullptr; + /// \sa make_client_history(), TrivialChangesetCooker. + std::shared_ptr changeset_cooker; + + /// The maximum number of milliseconds to allow for a connection to + /// become fully established. This includes the time to resolve the + /// network address, the TCP connect operation, the SSL handshake, and + /// the WebSocket handshake. + milliseconds_type connect_timeout = default_connect_timeout; + + /// The number of milliseconds to keep a connection open after all + /// sessions have been abandoned (or suspended by errors). + /// + /// The purpose of this linger time is to avoid close/reopen cycles + /// during short periods of time where there are no sessions interested + /// in using the connection. + /// + /// If the connection gets closed due to an error before the linger time + /// expires, the connection will be kept closed until there are sessions + /// willing to use it again. + milliseconds_type connection_linger_time = default_connection_linger_time; + + /// The client will send PING messages periodically to allow the server + /// to detect dead connections (heartbeat). This parameter specifies the + /// time, in milliseconds, between these PING messages. When scheduling + /// the next PING message, the client will deduct a small random amount + /// from the specified value to help spread the load on the server from + /// many clients. + milliseconds_type ping_keepalive_period = default_ping_keepalive_period; + + /// Whenever the server receives a PING message, it is supposed to + /// respond with a PONG messsage to allow the client to detect dead + /// connections (heartbeat). This parameter specifies the time, in + /// milliseconds, that the client will wait for the PONG response + /// message before it assumes that the connection is dead, and + /// terminates it. + milliseconds_type pong_keepalive_timeout = default_pong_keepalive_timeout; + + /// The maximum amount of time, in milliseconds, since the loss of a + /// prior connection, for a new connection to be considered a *fast + /// reconnect*. + /// + /// In general, when a client establishes a connection to the server, + /// the uploading process remains suspended until the initial + /// downloading process completes (as if by invocation of + /// Session::async_wait_for_download_completion()). However, to avoid + /// unnecessary latency in change propagation during ongoing + /// application-level activity, if the new connection is established + /// less than a certain amout of time (`fast_reconnect_limit`) since the + /// client was previously connected to the server, then the uploading + /// process will be activated immediately. + /// + /// For now, the purpose of the general delaying of the activation of + /// the uploading process, is to increase the chance of multiple initial + /// transactions on the client-side, to be uploaded to, and processed by + /// the server as a single unit. In the longer run, the intention is + /// that the client should upload transformed (from reciprocal history), + /// rather than original changesets when applicable to reduce the need + /// for changeset to be transformed on both sides. The delaying of the + /// upload process will increase the number of cases where this is + /// possible. + /// + /// FIXME: Currently, the time between connections is not tracked across + /// sessions, so if the application closes its session, and opens a new + /// one immediately afterwards, the activation of the upload process + /// will be delayed unconditionally. + milliseconds_type fast_reconnect_limit = default_fast_reconnect_limit; + + /// If enable_upload_log_compaction is true, every changeset will be + /// compacted before it is uploaded to the server. Compaction will + /// reduce the size of a changeset if the same field is set multiple + /// times or if newly created objects are deleted within the same + /// transaction. Log compaction increeses CPU usage and memory + /// consumption. + bool enable_upload_log_compaction = true; + + /// Set the `TCP_NODELAY` option on all TCP/IP sockets. This disables + /// the Nagle algorithm. Disabling it, can in some cases be used to + /// decrease latencies, but possibly at the expense of scalability. Be + /// sure to research the subject before you enable this option. + bool tcp_no_delay = false; + + /// The specified function will be called whenever a PONG message is + /// received on any connection. The round-trip time in milliseconds will + /// be pased to the function. The specified function will always be + /// called by the client's event loop thread, i.e., the thread that + /// calls `Client::run()`. This feature is mainly for testing purposes. + std::function roundtrip_time_handler; }; /// \throw util::EventLoop::Implementation::NotAvailable if no event loop @@ -150,9 +225,47 @@ class Client { /// /// This corresponds to calling Session::cancel_reconnect_delay() on all /// bound sessions, but will also cancel reconnect delays applying to - // servers for which there are currently no bound sessions. + /// servers for which there are currently no bound sessions. + /// + /// Thread-safe. void cancel_reconnect_delay(); + /// \brief Wait for session termination to complete. + /// + /// Wait for termination of all sessions whose termination was initiated + /// prior this call (the completion condition), or until the client's event + /// loop thread exits from Client::run(), whichever happens + /// first. Termination of a session can be initiated implicitly (e.g., via + /// destruction of the session object), or explicitly by Session::detach(). + /// + /// Note: After session termination (when this function returns true) no + /// session specific callback function can be called or continue to execute, + /// and the client is guaranteed to no longer have a Realm file open on + /// behalf of the terminated session. + /// + /// CAUTION: If run() returns while a wait operation is in progress, this + /// waiting function will return immediately, even if the completion + /// condition is not yet satisfied. The completion condition is guaranteed + /// to be satisfied only when these functions return true. If it returns + /// false, session specific callback functions may still be executing or get + /// called, and the associated Realm files may still not have been closed. + /// + /// If a new wait operation is initiated while another wait operation is in + /// progress by another thread, the waiting period of fist operation may, or + /// may not get extended. The application must not assume either. + /// + /// Note: Session termination does not imply that the client has received an + /// UNBOUND message from the server (see the protocol specification). This + /// may happen later. + /// + /// \return True only if the completion condition was satisfied. False if + /// the client's event loop thread exited from Client::run() in which case + /// the completion condition may, or may not have been satisfied. + /// + /// Note: These functions are fully thread-safe. That is, they may be called + /// by any thread, and by multiple threads concurrently. + bool wait_for_session_terminations_or_client_stopped(); + private: class Impl; std::unique_ptr m_impl; @@ -193,51 +306,221 @@ class BadServerUrl; // Exception /// execution of bind() starts, i.e., before bind() returns. /// /// At most one session is allowed to exist for a particular local Realm file -/// (file system inode) at any point in time. Multiple objects may coexists, as -/// long as bind() has been called on at most one of them. Additionally, two -/// sessions are allowed to exist at different times, and with no overlap in -/// time, as long as they are associated with the same client object, or with -/// two different client objects that do not overlap in time. This means, in -/// particular, that it is an error to create two nonoverlapping sessions for -/// the same local Realm file, it they are associated with two different client -/// objects that overlap in time. It is the responsibility of the application to -/// ensure that these rules are adhered to. The consequences of a violation are -/// unspecified. +/// (file system inode) at any point in time. Multiple session objects may +/// coexists for a single file, as long as bind() has been called on at most one +/// of them. Additionally, two bound session objects for the same file are +/// allowed to exist at different times, if they have no overlap in time (in +/// their bound state), as long as they are associated with the same client +/// object, or with two different client objects that do not overlap in +/// time. This means, in particular, that it is an error to create two bound +/// session objects for the same local Realm file, it they are associated with +/// two different client objects that overlap in time, even if the session +/// objects do not overlap in time (in their bound state). It is the +/// responsibility of the application to ensure that these rules are adhered +/// to. The consequences of a violation are unspecified. /// /// Thread-safety: It is safe for multiple threads to construct, use (with some /// exceptions), and destroy session objects concurrently, regardless of whether /// those session objects are associated with the same, or with different Client /// objects. Please note that some of the public member functions are fully /// thread-safe, while others are not. +/// +/// Callback semantics: All session specific callback functions will be executed +/// by the event loop thread, i.e., the thread that calls Client::run(). No +/// callback function will be called before Session::bind() is called. Callback +/// functions that are specified prior to calling bind() (e.g., any passed to +/// set_progress_handler()) may start to execute before bind() returns, as long +/// as some thread is executing Client::run(). Likewise, completion handlers, +/// such as those passed to async_wait_for_sync_completion() may start to +/// execute before the submitting function returns. All session specific +/// callback functions (including completion handlers) are guaranteed to no +/// longer be executing when session termination completes, and they are +/// guaranteed to not be called after session termination completes. Termination +/// is an event that completes asynchronously with respect to the application, +/// but is initiated by calling detach(), or implicitely by destroying a session +/// object. After having initiated one or more session terminations, the +/// application can wait for those terminations to complete by calling +/// Client::wait_for_session_terminations_or_client_stopped(). Since callback +/// functinos are always executed by the event loop thread, they are also +/// guaranteed to not be executing after Client::run() has returned. class Session { public: using port_type = util::network::Endpoint::port_type; - using version_type = _impl::History::version_type; using SyncTransactCallback = void(VersionID old_version, VersionID new_version); using ProgressHandler = void(std::uint_fast64_t downloaded_bytes, std::uint_fast64_t downloadable_bytes, std::uint_fast64_t uploaded_bytes, - std::uint_fast64_t uploadable_bytes); + std::uint_fast64_t uploadable_bytes, + std::uint_fast64_t progress_version, + std::uint_fast64_t snapshot_version); using WaitOperCompletionHandler = std::function; + using SSLVerifyCallback = bool(const std::string& server_address, + port_type server_port, + const char* pem_data, + size_t pem_size, + int preverify_ok, + int depth); class Config { public: Config() {} + /// server_address is the fully qualified host name, or IP address of + /// the server. + std::string server_address = "localhost"; + + /// server_port is the port at which the server listens. If server_port + /// is zero, the default port for the specified protocol is used. See \ref + /// Protocol for information on default ports. + port_type server_port = 0; + + /// server_path is the virtual path by which the server identifies the + /// Realm. This path must always be an absolute path, and must therefore + /// always contain a leading slash (`/`). Further more, each segment of the + /// virtual path must consist of one or more characters that are either + /// alpha-numeric or in (`_`, `-`, `.`), and each segment is not allowed to + /// equal `.` or `..`, and must not end with `.realm`, `.realm.lock`, or + /// `.realm.management`. These rules are necessary because the server + /// currently reserves the right to use the specified path as part of the + /// file system path of a Realm file. It is expected that these rules will + /// be significantly relaxed in the future by completely decoupling the + /// virtual paths from actual file system paths. + std::string server_path = "/"; + + /// The protocol used for communicating with the server. See \ref Protocol. + Protocol protocol = Protocol::realm; + + /// url_prefix is a prefix that is prepended to the server_path + /// in the HTTP GET request that initiates a sync connection. The value + /// specified here must match with the server's expectation. Changing + /// the value of url_prefix should be matched with a corresponding + /// change of the server side proxy. + std::string url_prefix = "/realm-sync"; + + /// authorization_header_name is the name of the HTTP header containing + /// the Realm access token. The value of the HTTP header is + /// "Realm-Access-Token version=1 token=....". + /// authorization_header_name does not participate in session + /// multiplexing partitioning. + std::string authorization_header_name = "Authorization"; + + /// custom_http_headers is a map of custom HTTP headers. The keys of the map + /// are HTTP header names, and the values are the corresponding HTTP + /// header values. + /// If "Authorization" is used as a custom header name, + /// authorization_header_name must be set to anther value. + std::map custom_http_headers; + + /// Sessions can be multiplexed over the same TCP/SSL connection. + /// Sessions might share connection if they have identical server_address, + /// server_port, and protocol. multiplex_ident is a parameter that allows + /// finer control over session multiplexing. If two sessions have distinct + /// multiplex_ident, they will never share connection. The typical use of + /// multilex_ident is to give sessions with incompatible SSL requirements + /// distinct multiplex_idents. + /// multiplex_ident can be any string and the value has no meaning except + /// for partitioning the sessions. + std::string multiplex_ident; + + /// verify_servers_ssl_certificate controls whether the server + /// certificate is verified for SSL connections. It should generally be + /// true in production. + bool verify_servers_ssl_certificate = true; + + /// ssl_trust_certificate_path is the path of a trust/anchor + /// certificate used by the client to verify the server certificate. + /// ssl_trust_certificate_path is only used if the protocol is ssl and + /// verify_servers_ssl_certificate is true. + /// + /// A server certificate is verified by first checking that the + /// certificate has a valid signature chain back to a trust/anchor + /// certificate, and secondly checking that the server_address matches + /// a host name contained in the certificate. The host name of the + /// certificate is stored in either Common Name or the Alternative + /// Subject Name (DNS section). + /// + /// If ssl_trust_certificate_path is None (default), ssl_verify_callback + /// (see below) is used if set, and the default device trust/anchor + /// store is used otherwise. + Optional ssl_trust_certificate_path; + + /// If Client::Config::ssl_verify_callback is set, that function is called + /// to verify the certificate, unless verify_servers_ssl_certificate is + /// false. + + /// ssl_verify_callback is used to implement custom SSL certificate + /// verification. it is only used if the protocol is SSL, + /// verify_servers_ssl_certificate is true and ssl_trust_certificate_path + /// is None. + /// + /// The signature of ssl_verify_callback is + /// + /// bool(const std::string& server_address, + /// port_type server_port, + /// const char* pem_data, + /// size_t pem_size, + /// int preverify_ok, + /// int depth); + /// + /// server address and server_port is the address and port of the server + /// that a SSL connection is being established to. They are identical to + /// the server_address and server_port set in this config file and are + /// passed for convenience. + /// pem_data is the certificate of length pem_size in + /// the PEM format. preverify_ok is OpenSSL's preverification of the + /// certificate. preverify_ok is either 0, or 1. If preverify_ok is 1, + /// OpenSSL has accepted the certificate and it will generally be safe + /// to trust that certificate. depth represents the position of the + /// certificate in the certificate chain sent by the server. depth = 0 + /// represents the actual server certificate that should contain the + /// host name(server address) of the server. The highest depth is the + /// root certificate. + /// The callback function will receive the certificates starting from + /// the root certificate and moving down the chain until it reaches the + /// server's own certificate with a host name. The depth of the last + /// certificate is 0. The depth of the first certificate is chain + /// length - 1. + /// + /// The return value of the callback function decides whether the + /// client accepts the certificate. If the return value is false, the + /// processing of the certificate chain is interrupted and the SSL + /// connection is rejected. If the return value is true, the verification + /// process continues. If the callback function returns true for all + /// presented certificates including the depth == 0 certificate, the + /// SSL connection is accepted. + /// + /// A recommended way of using the callback function is to return true + /// if preverify_ok = 1 and depth > 0, + /// always check the host name if depth = 0, + /// and use an independent verification step if preverify_ok = 0. + /// + /// Another possible way of using the callback is to collect all the + /// certificates until depth = 0, and present the entire chain for + /// independent verification. + std::function ssl_verify_callback; + + /// signed_user_token is a cryptographically signed token describing the + /// identity and access rights of the current user. + std::string signed_user_token; + /// If not null, overrides whatever is specified by /// Client::Config::changeset_cooker. /// - /// CAUTION: The ChangesetCooker::cook_changeset() may be called on the - /// specified object before the call to bind() returns, and it may be + /// The shared ownership over the cooker will be relinquished shortly + /// after the destruction of the session object as long as the event + /// loop of the client is being executed (Client::run()). + /// + /// CAUTION: ChangesetCooker::cook_changeset() of the specified cooker + /// may get called before the call to bind() returns, and it may get /// called (or continue to execute) after the session object is - /// destroyed. The application must specify an object for which that - /// function can be safely be called, and continue to execute from the - /// point in time where bind() starts executing, and up until the point - /// in time where the last invocation of `clint.run()` returns. Here, - /// `client` refers to the associated Client object. + /// destroyed. Please see "Callback semantics" section under Client for + /// more on this. /// - /// \sa make_sync_history(), TrivialChangesetCooker. - SyncHistory::ChangesetCooker* changeset_cooker = nullptr; + /// \sa make_client_history(), TrivialChangesetCooker. + std::shared_ptr changeset_cooker; + + /// The encryption key the SharedGroup will be opened with. + Optional> encryption_key; }; /// \brief Start a new session for the specified client-side Realm. @@ -250,10 +533,42 @@ class Session { /// file. Session(Client&, std::string realm_path, Config = {}); + /// This leaves the right-hand side session object detached. See "Thread + /// safety" section under detach(). Session(Session&&) noexcept; + /// Create a detached session object (see detach()). + Session() noexcept; + + /// Implies detachment. See "Thread safety" section under detach(). ~Session() noexcept; + /// Detach the object on the left-hand side, then "steal" the session from + /// the object on the right-hand side, if there is one. This leaves the + /// object on the right-hand side detached. See "Thread safety" section + /// under detach(). + Session& operator=(Session&&) noexcept; + + /// Detach this sesion object from the client object (Client). If the + /// session object is already detached, this function has no effect + /// (idempotency). + /// + /// Detachment initiates session termination, which is an event that takes + /// place shortly therafter in the context of the client's event loop + /// thread. + /// + /// A detached session object may be destroyed, move-assigned to, and moved + /// from. Apart from that, it is an error to call any function other than + /// detach() on a detached session object. + /// + /// Thread safety: Detachment is not a thread-safe operation. This means + /// that detach() may not be executed by two threads concurrently, and may + /// not execute concurrently with object destruction. Additionally, + /// detachment must not execute concurrently with a moving operation + /// involving the session object on the left or right-hand side. See move + /// constructor and assigment operator. + void detach() noexcept; + /// \brief Set a function to be called when the local Realm has changed due /// to integration of a downloaded changeset. /// @@ -274,13 +589,10 @@ class Session { /// it is called while another thread is executing any member function on /// the same Session object. /// - /// CAUTION: The specified callback function may be called before the call - /// to bind() returns, and it may be called (or continue to execute) after - /// the session object is destroyed. The application must pass a handler - /// that can be safely called, and can safely continue to execute from the - /// point in time where bind() starts executing, and up until the point in - /// time where the last invocation of `client.run()` returns. Here, `client` - /// refers to the associated Client object. + /// CAUTION: The specified callback function may get called before the call + /// to bind() returns, and it may get called (or continue to execute) after + /// the session object is destroyed. Please see "Callback semantics" section + /// under Session for more on this. void set_sync_transact_callback(std::function); /// \brief Set a handler to monitor the state of download and upload @@ -289,7 +601,8 @@ class Session { /// The handler must have signature /// /// void(uint_fast64_t downloaded_bytes, uint_fast64_t downloadable_bytes, - /// uint_fast64_t uploaded_bytes, uint_fast64_t uploadable_bytes); + /// uint_fast64_t uploaded_bytes, uint_fast64_t uploadable_bytes, + /// uint_fast64_t progress_version); /// /// downloaded_bytes is the size in bytes of all downloaded changesets. /// downloadable_bytes is the size in bytes of the part of the server @@ -335,59 +648,80 @@ class Session { /// ----------------------------------------------- /// (downloadable_bytes - initial_downloaded_bytes) /// - /// The handler is called on the event loop thread. - /// The handler is called after or during set_progress_handler(), - /// after bind(), after each DOWNLOAD message, and after each local - /// transaction (nonsync_transact_notify). + /// progress_version is 0 at the start of a session. When at least one + /// DOWNLOAD message has been received from the server, progress_version is + /// positive. progress_version can be used to ensure that the reported + /// progress contains information obtained from the server in the current + /// session. The server will send a message as soon as possible, and the + /// progress handler will eventually be called with a positive progress_version + /// unless the session is interrupted before a message from the server has + /// been received. + /// + /// The handler is called on the event loop thread.The handler after bind(), + /// after each DOWNLOAD message, and after each local transaction + /// (nonsync_transact_notify). /// /// set_progress_handler() is not thread safe and it must be called before /// bind() is called. Subsequent calls to set_progress_handler() overwrite /// the previous calls. Typically, this function is called once per session. /// - /// The progress handler is also posted to the event loop during the - /// execution of set_progress_handler(). - /// - /// CAUTION: The specified callback function may be called before the call - /// to set_progress_handler() returns, and it may be called - /// (or continue to execute) after the session object is destroyed. - /// The application must pass a handler that can be safely called, and can - /// execute from the point in time where set_progress_handler() is called, - /// and up until the point in time where the last invocation of - /// `client.run()` returns. Here, `client` refers to the associated - /// Client object. + /// CAUTION: The specified callback function may get called before the call + /// to bind() returns, and it may get called (or continue to execute) after + /// the session object is destroyed. Please see "Callback semantics" section + /// under Session for more on this. void set_progress_handler(std::function); - /// \brief Signature of an error handler. - /// - /// \param ec The error code. For the list of errors reported by the server, - /// see \ref ProtocolError (or `protocol.md`). For the list of errors - /// corresponding with protocol violation that are detected by the client, - /// see Client::Error. - /// - /// \param is_fatal The error is of a kind that is likely to persist, and - /// cause all future reconnect attempts to fail. The client may choose to - /// try to reconnect again later, but if so, the waiting period will be - /// substantial. - /// - /// \param detailed_message The message associated with the error. It is - /// usually equal to `ec.message()`, but may also be a more specific message - /// (one that provides extra context). The purpose of this message is mostly - /// to aid debugging. For non-debugging purposes, `ec.message()` should - /// generally be considered sufficient. - /// - /// \sa set_error_handler(). - using ErrorHandler = void(std::error_code ec, bool is_fatal, - const std::string& detailed_message); + enum class ConnectionState { disconnected, connecting, connected }; + + /// \brief Information about an error causing a session to be temporarily + /// disconnected from the server. + /// + /// In general, the connection will be automatically reestablished + /// later. Whether this happens quickly, generally depends on \ref + /// is_fatal. If \ref is_fatal is true, it means that the error is deemed to + /// be of a kind that is likely to persist, and cause all future reconnect + /// attempts to fail. In that case, if another attempt is made at + /// reconnecting, the delay will be substantial (at least an hour). + /// + /// \ref error_code specifies the error that caused the connection to be + /// closed. For the list of errors reported by the server, see \ref + /// ProtocolError (or `protocol.md`). For the list of errors corresponding + /// to protocol violations that are detected by the client, see + /// Client::Error. The error may also be a system level error, or an error + /// from one of the potential intermediate protocol layers (SSL or + /// WebSocket). + /// + /// \ref detailed_message is the most detailed message available to describe + /// the error. It is generally equal to `error_code.message()`, but may also + /// be a more specific message (one that provides extra context). The + /// purpose of this message is mostly to aid in debugging. For non-debugging + /// purposes, `error_code.message()` should generally be considered + /// sufficient. + /// + /// \sa set_connection_state_change_listener(). + struct ErrorInfo { + std::error_code error_code; + bool is_fatal; + const std::string& detailed_message; + }; + + using ConnectionStateChangeListener = void(ConnectionState, const ErrorInfo*); - /// \brief Set the error handler for this session. + /// \brief Install a connection state change listener. /// - /// Sets a function to be called when an error causes a connection - /// initiation attempt to fail, or an established connection to be broken. + /// Sets a function to be called whenever the state of the underlying + /// network connection changes between "disconnected", "connecting", and + /// "connected". The initial state is always "disconnected". The next state + /// after "disconnected" is always "connecting". The next state after + /// "connecting" is either "connected" or "disconnected". The next state + /// after "connected" is always "disconnected". A switch to the + /// "disconnected" state only happens when an error occurs. /// - /// When a connection is established on behalf of multiple sessions, a - /// connection-level error will be reported to all those sessions. A - /// session-level error, on the other hand, will only be reported to the - /// affected session. + /// Whenever the installed function is called, an ErrorInfo object is passed + /// when, and only when the passed state is ConnectionState::disconnected. + /// + /// When multiple sessions share a single connection, the state changes will + /// be reported for each session in turn. /// /// The callback function will always be called by the thread that executes /// the event loop (Client::run()), but not until bind() is called. If the @@ -402,14 +736,17 @@ class Session { /// it is called while another thread is executing any member function on /// the same Session object. /// - /// CAUTION: The specified callback function may be called before the call - /// to bind() returns, and it may be called (or continue to execute) after - /// the session object is destroyed. The application must pass a handler - /// that can be safely called, and can safely continue to execute from the - /// point in time where bind() starts executing, and up until the point in - /// time where the last invocation of `client.run()` returns. Here, `client` - /// refers to the associated Client object. + /// CAUTION: The specified callback function may get called before the call + /// to bind() returns, and it may get called (or continue to execute) after + /// the session object is destroyed. Please see "Callback semantics" section + /// under Session for more on this. + void set_connection_state_change_listener(std::function); + + //@{ + /// Deprecated! Use set_connection_state_change_listener() instead. + using ErrorHandler = void(std::error_code, bool is_fatal, const std::string& detailed_message); void set_error_handler(std::function); + //@} /// @{ \brief Bind this session to the specified server side Realm. /// @@ -430,53 +767,54 @@ class Session { /// it is called while another thread is executing any member function on /// the same Session object. /// - /// \param server_url For example "realm://sync.realm.io/test". See \a - /// server_address, \a server_path, and \a server_port for information about - /// the individual components of the URL. See \ref Protocol for the list of - /// available URL schemes and the associated default ports. The 2-argument - /// version has exactly the same affect as the 5-argument version. - /// - /// \param server_address The fully qualified host name, or IP address of - /// the server. - /// - /// \param server_path The virtual path by which the server identifies the - /// Realm. This path must always be an absolute path, and must therefore - /// always contain a leading slash (`/`). Further more, each segment of the - /// virtual path must consist of one or more characters that are either - /// alpha-numeric or in (`_`, `-`, `.`), and each segment is not allowed to - /// equal `.` or `..`, and must not end with `.realm`, `.realm.lock`, or - /// `.realm.management`. These rules are necessary because the server - /// currently reserves the right to use the specified path as part of the - /// file system path of a Realm file. It is expected that these rules will - /// be significantly relaxed in the future by completely decoupling the - /// virtual paths from actual file system paths. - /// - /// \param signed_user_token A cryptographically signed token describing the - /// identity and access rights of the current user. See \ref Protocol. + /// bind() binds this session to the specified server side Realm using the + /// parameters specified in the Session::Config object. /// - /// \param server_port If zero, use the default port for the specified - /// protocol. See \ref Protocol for information on default ports. + /// The two other forms of bind() are convenience functions. + /// void bind(std::string server_address, std::string server_path, + /// std::string signed_user_token, port_type server_port = 0, + /// Protocol protocol = Protocol::realm); + /// replaces the corresponding parameters from the Session::Config object + /// before the session is bound. + /// void bind(std::string server_url, std::string signed_user_token) parses + /// the \param server_url and replaces the parameters in the Session::Config object + /// before the session is bound. /// - /// \param protocol See \ref Protocol. + /// \param server_url For example "realm://sync.realm.io/test". See + /// server_address, server_path, and server_port in Session::Config for information + /// about the individual components of the URL. See \ref Protocol for the list of + /// available URL schemes and the associated default ports. /// /// \throw BadServerUrl if the specified server URL is malformed. + void bind(); void bind(std::string server_url, std::string signed_user_token); void bind(std::string server_address, std::string server_path, std::string signed_user_token, port_type server_port = 0, Protocol protocol = Protocol::realm); /// @} - /// \brief Refresh the user token associated with this session. + /// \brief Refresh the access token associated with this session. /// /// This causes the REFRESH protocol message to be sent to the server. See /// \ref Protocol. /// - /// In an on-going session a client may expect its access token to expire at - /// a certain time and schedule acquisition of a fresh access token (using a - /// refresh token or by other means) in due time to provide a better user - /// experience. Without refreshing the token, the client will be notified - /// that the session is terminated due to insufficient privileges and must - /// reacquire a fresh token, which is a potentially disruptive process. + /// In an on-going session the application may expect the access token to + /// expire at a certain time and schedule acquisition of a fresh access + /// token (using a refresh token or by other means) in due time to provide a + /// better user experience, and seamless connectivity to the server. + /// + /// If the application does not proactively refresh an expiring token, the + /// session will eventually be disconnected. The application can detect this + /// by monitoring the connection state + /// (set_connection_state_change_listener()), and check whether the error + /// code is `ProtocolError::token_expired`. Such a session can then be + /// revived by calling refresh() with a newly acquired access token. + /// + /// Due to protocol techicalities, a race condition exists that can cause a + /// session to become, and remain disconnected after a new access token has + /// been passed to refresh(). The application can work around this race + /// condition by detecting the `ProtocolError::token_expired` error, and + /// always initiate a token renewal in this case. /// /// It is an error to call this function before calling `Client::bind()`. /// @@ -489,7 +827,7 @@ class Session { /// \brief Inform the synchronization agent about changes of local origin. /// /// This function must be called by the application after a transaction - /// performed on its behlaf, that is, after a transaction that is not + /// performed on its behalf, that is, after a transaction that is not /// performed to integrate a changeset that was downloaded from the server. /// /// It is an error to call this function before bind() has been called, and @@ -516,8 +854,8 @@ class Session { /// i.e., prior to the invocation of async_wait_for_upload_completion() or /// async_wait_for_sync_completion(). Unannounced changesets may get picked /// up, but there is no guarantee that they will be, however, if a certain - /// changeset is announced, then all previous changesets are implicitely - /// announced. Also all preexisting changesets are implicitely announced + /// changeset is announced, then all previous changesets are implicitly + /// announced. Also all preexisting changesets are implicitly announced /// when the session is initiated. /// /// Download is considered complete when all non-empty changesets of remote @@ -530,11 +868,12 @@ class Session { /// async_wait_for_sync_completion() therefore requires a full client <-> /// server round-trip. /// - /// If a new wait operation is initiated while other wait operations are in - /// progress, the waiting period of operations in progress may, or may not - /// get extended. The client must not assume either. The client may assume, - /// however, that async_wait_for_upload_completion() will not affect the - /// waiting period of async_wait_for_download_completion(), and vice versa. + /// If a new wait operation is initiated while another wait operation is in + /// progress by another thread, the waiting period of first operation may, + /// or may not get extended. The application must not assume either. The + /// application may assume, however, that async_wait_for_upload_completion() + /// will not affect the waiting period of + /// async_wait_for_download_completion(), and vice versa. /// /// It is an error to call these functions before bind() has been called, /// and has returned. @@ -554,15 +893,10 @@ class Session { /// loop thread is running, all completion handlers will be called /// regardless of whether the operations get canceled or not. /// - /// CAUTION: The specified completion handlers may be called before the - /// initiation function returns (e.g. before - /// async_wait_for_upload_completion() returns), and it may be called (or - /// continue to execute) after the session object is destroyed. The - /// application must pass a handler that can be safely called, and can - /// safely continue to execute from the point in time where the initiating - /// function starts executing, and up until the point in time where the last - /// invocation of `clint.run()` returns. Here, `client` refers to the - /// associated Client object. + /// CAUTION: The specified completion handlers may get called before the + /// call to the waiting function returns, and it may get called (or continue + /// to execute) after the session object is destroyed. Please see "Callback + /// semantics" section under Session for more on this. /// /// Note: These functions are fully thread-safe. That is, they may be called /// by any thread, and by multiple threads concurrently. @@ -577,15 +911,25 @@ class Session { /// async_wait_for_upload_completion() and /// async_wait_for_download_completion() respectively. This means that they /// block the caller until the completion condition is satisfied, or the - /// client event loop is stopped (Client::stop()). + /// client's event loop thread exits from Client::run(), whichever happens + /// first. /// /// It is an error to call these functions before bind() has been called, /// and has returned. /// + /// CAUTION: If Client::run() returns while a wait operation is in progress, + /// these waiting functions return immediately, even if the completion + /// condition is not yet satisfied. The completion condition is guaranteed + /// to be satisfied only when these functions return true. + /// + /// \return True only if the completion condition was satisfied. False if + /// the client's event loop thread exited from Client::run() in which case + /// the completion condition may, or may not have been satisfied. + /// /// Note: These functions are fully thread-safe. That is, they may be called /// by any thread, and by multiple threads concurrently. - void wait_for_upload_complete_or_client_stopped(); - void wait_for_download_complete_or_client_stopped(); + bool wait_for_upload_complete_or_client_stopped(); + bool wait_for_download_complete_or_client_stopped(); /// @} /// \brief Cancel the current or next reconnect delay for the server @@ -611,10 +955,14 @@ class Session { /// thread, and by multiple threads concurrently. void cancel_reconnect_delay(); + /// \brief Change address of server for this session. + void override_server(std::string address, port_type); + private: class Impl; - Impl* m_impl; + Impl* m_impl = nullptr; + void abandon() noexcept; void async_wait_for(bool upload_completion, bool download_completion, WaitOperCompletionHandler); }; @@ -624,7 +972,8 @@ class Session { /// /// These errors will terminate the network connection (disconnect all sessions /// associated with the affected connection), and the error will be reported to -/// the application via the error handlers of the affected sessions. +/// the application via the connection state change listeners of the affected +/// sessions. enum class Client::Error { connection_closed = 100, ///< Connection closed (no error) unknown_message = 101, ///< Unknown type of input message @@ -632,7 +981,7 @@ enum class Client::Error { limits_exceeded = 103, ///< Limits exceeded in input message bad_session_ident = 104, ///< Bad session identifier in input message bad_message_order = 105, ///< Bad input message order - bad_file_ident_pair = 106, ///< Bad file identifier pair (ALLOC) + bad_client_file_ident = 106, ///< Bad client file identifier (IDENT) bad_progress = 107, ///< Bad progress information (DOWNLOAD) bad_changeset_header_syntax = 108, ///< Bad syntax in changeset header (DOWNLOAD) bad_changeset_size = 109, ///< Bad changeset size in changeset header (DOWNLOAD) @@ -642,6 +991,13 @@ enum class Client::Error { bad_request_ident = 113, ///< Bad request identifier (MARK) bad_error_code = 114, ///< Bad error code (ERROR), bad_compression = 115, ///< Bad compression (DOWNLOAD) + bad_client_version = 116, ///< Bad last integrated client version in changeset header (DOWNLOAD) + ssl_server_cert_rejected = 117, ///< SSL server certificate rejected + pong_timeout = 118, ///< Timeout on reception of PONG respone message + bad_client_file_ident_salt = 119, ///< Bad client file identifier salt (IDENT) + bad_file_ident = 120, ///< Bad file identifier (ALLOC) + connect_timeout = 121, ///< Sync connection was not fully established in time + bad_timestamp = 122, ///< Bad timestamp (PONG) }; const std::error_category& client_error_category() noexcept; @@ -674,6 +1030,53 @@ class BadServerUrl: public std::exception { } }; +inline Session::Session(Session&& sess) noexcept: + m_impl{sess.m_impl} +{ + sess.m_impl = nullptr; +} + +inline Session::Session() noexcept +{ +} + +inline Session::~Session() noexcept +{ + if (m_impl) + abandon(); +} + +inline Session& Session::operator=(Session&& sess) noexcept +{ + if (m_impl) + abandon(); + m_impl = sess.m_impl; + sess.m_impl = nullptr; + return *this; +} + +inline void Session::detach() noexcept +{ + if (m_impl) + abandon(); + m_impl = nullptr; +} + +inline void Session::set_error_handler(std::function handler) +{ + auto handler_2 = [handler=std::move(handler)](ConnectionState state, + const ErrorInfo* error_info) { + if (state != ConnectionState::disconnected) + return; + REALM_ASSERT(error_info); + std::error_code ec = error_info->error_code; + bool is_fatal = error_info->is_fatal; + const std::string& detailed_message = error_info->detailed_message; + handler(ec, is_fatal, detailed_message); // Throws + }; + set_connection_state_change_listener(std::move(handler_2)); // Throws +} + inline void Session::async_wait_for_sync_completion(WaitOperCompletionHandler handler) { bool upload_completion = true, download_completion = true; diff --git a/Pods/Realm/include/core/realm/sync/crypto.hpp b/Pods/Realm/include/core/realm/sync/crypto.hpp index 016543e..81a92fe 100644 --- a/Pods/Realm/include/core/realm/sync/crypto.hpp +++ b/Pods/Realm/include/core/realm/sync/crypto.hpp @@ -1,11 +1,11 @@ /************************************************************************* - * + * * REALM CONFIDENTIAL * __________________ - * + * * [2011] - [2015] Realm Inc * All Rights Reserved. - * + * * NOTICE: All information contained herein is, and remains * the property of Realm Incorporated and its suppliers, * if any. The intellectual and technical concepts contained diff --git a/Pods/Realm/include/core/realm/sync/crypto_server.hpp b/Pods/Realm/include/core/realm/sync/crypto_server.hpp index f2ac3f0..c241a71 100644 --- a/Pods/Realm/include/core/realm/sync/crypto_server.hpp +++ b/Pods/Realm/include/core/realm/sync/crypto_server.hpp @@ -49,8 +49,13 @@ class PKey { /// Load RSA public key from \a pemfile. static PKey load_public(const std::string& pemfile); + /// Load RSA public key from a PEM buffer + static PKey load_public(BinaryData pem_buffer); + /// Load RSA public/private keypair from \a pemfile. static PKey load_private(const std::string& pemfile); + /// Load RSA public/private keypair from a PEM buffer + static PKey load_private(BinaryData pem_buffer); /// Whether or not the key can be used for signing. /// diff --git a/Pods/Realm/include/core/realm/sync/feature_token.hpp b/Pods/Realm/include/core/realm/sync/feature_token.hpp new file mode 100644 index 0000000..86db015 --- /dev/null +++ b/Pods/Realm/include/core/realm/sync/feature_token.hpp @@ -0,0 +1,69 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2012] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ +#ifndef REALM_SYNC_FEATURE_TOKEN_HPP +#define REALM_SYNC_FEATURE_TOKEN_HPP + +#include + +#if !REALM_MOBILE && !defined(REALM_EXCLUDE_FEATURE_TOKENS) +#define REALM_HAVE_FEATURE_TOKENS 1 +#else +#define REALM_HAVE_FEATURE_TOKENS 0 +#endif + +#if REALM_HAVE_FEATURE_TOKENS + +#include + +#include + +namespace realm { +namespace sync { + +class FeatureGate { +public: + + // The constructor takes a JWT token as argument. + // The constructor throws a std::runtime_error if + // the token is invalid. An invalid token is a token + // that has bad syntax, is not signed by Realm, or is + // expired. + FeatureGate(StringData token); + + // Constructs a feature gate without any features. + FeatureGate(); + ~FeatureGate(); + + FeatureGate(FeatureGate&&); + FeatureGate& operator=(FeatureGate&&); + + bool has_feature(StringData feature_name); + +private: + struct Impl; + std::unique_ptr m_impl; +}; + + +} // namespace sync +} // namespace realm + +#endif // REALM_HAVE_FEATURE_TOKENS +#endif // REALM_SYNC_FEATURE_TOKEN_HPP diff --git a/Pods/Realm/include/core/realm/sync/history.hpp b/Pods/Realm/include/core/realm/sync/history.hpp index e93b293..d989e3f 100644 --- a/Pods/Realm/include/core/realm/sync/history.hpp +++ b/Pods/Realm/include/core/realm/sync/history.hpp @@ -21,7 +21,9 @@ #include #include -#include +#include +#include +#include #include #ifndef REALM_SYNC_HISTORY_HPP @@ -30,76 +32,47 @@ namespace realm { namespace sync { -/// SyncProgress is the progress sent by the server in the download message. The -/// server scans through its history in connection with every download message. -/// scan_server_version is the server_version of the changeset at which the -/// server ended the scan. scan_client_version is the client_version for this -/// client that was last integrated before scan_server_version. -/// latest_server_version is the end of the server history, and -/// latest_server_session_ident is the server_session_ident corresponding to -/// latest_sever_version. latest_client_version is the corresponding -/// client_version. In other words, latest_client_version is the very latest -/// version of a changeset originating from this client. -/// -/// The client persists the entire progress. It is not very important to persist -/// latest_server_version, but for consistency the entire progress is persisted. -struct SyncProgress { - using version_type = HistoryEntry::version_type; - - version_type scan_server_version = 0; - version_type scan_client_version = 0; - version_type latest_server_version = 0; - std::int_fast64_t latest_server_session_ident = 0; - version_type latest_client_version = 0; - int_fast64_t downloadable_bytes = 0; -}; - - -class SyncHistory: - public TrivialReplication { +class ClientHistoryBase : + public InstructionReplication { public: - using version_type = TrivialReplication::version_type; - using file_ident_type = HistoryEntry::file_ident_type; using SyncTransactCallback = void(VersionID old_version, VersionID new_version); - class ChangesetCooker; - class Config; - /// Get the version of the latest snapshot of the associated Realm, as well - /// as the file identifier pair and the synchronization progress pair as - /// they are stored in that snapshot. - /// - /// The returned current client version is the version of the latest - /// snapshot of the associated SharedGroup object, and is guaranteed to be - /// zero for the initial empty Realm state. - /// - /// The returned file identifier pair (server, client) is the one that was - /// last stored by set_file_ident_pair(). If no identifier pair has been - /// stored yet, \a client_file_ident is set to zero. + /// as the client file identifier and the synchronization progress as they + /// are stored in that snapshot. + /// + /// Note: The value of `progress.upload.last_integrated_server_version` is + /// not important to the caller, as long as the caller is the + /// synchronization client (`_impl::ClientImplBase`). The caller merely + /// passes the returned value back to find_uploadable_changesets() or + /// set_sync_progress(), so if the history implementation does not care + /// about the value of `progress.upload.last_integrated_server_version`, it + /// is allowed to not persist it. However, the implementation of + /// find_uploadable_changesets() must still be prepared for its value to be + /// determined by an incoming DOWNLAOD message, rather than being whatever + /// was returned by get_status(). + /// + /// The returned current client version is the version produced by the last + /// changeset in the history. The type of version returned here, is the one + /// that identifies an entry in the sync history. Whether this is the same + /// as the snapshot number of the Realm file depends on the history + /// implementation. + /// + /// The returned client file identifier is the one that was last stored by + /// set_client_file_ident(). If no identifier has been stored yet, the + /// `ident` field of \a client_file_ident is set to zero. /// /// The returned SyncProgress is the one that was last stored by /// set_sync_progress(), or {} if set_sync_progress() has never been called /// for the associated Realm file. virtual void get_status(version_type& current_client_version, - file_ident_type& server_file_ident, - file_ident_type& client_file_ident, - std::int_fast64_t& client_file_ident_secret, - SyncProgress& progress) = 0; - - /// Stores the server assigned file identifier pair (server, client) in the - /// associated Realm file, such that it is available via get_status() during - /// future synchronization sessions. It is an error to set this identifier - /// pair more than once per Realm file. - /// - /// \param server_file_ident The server assigned server-side file - /// identifier. This can be any non-zero integer strictly less than 2**64. - /// The server is supposed to choose a cryptic value that cannot easily be - /// guessed by clients (intentionally or not), and its only purpose is to - /// provide a higher level of fidelity during subsequent identification of - /// the server Realm. The server does not have to guarantee that this - /// identifier is unique, but in almost all cases it will be. Since the - /// client will also always specify the path name when referring to a server - /// file, the lack of a uniqueness guarantee is effectively not a problem. + SaltedFileIdent& client_file_ident, + SyncProgress& progress) const = 0; + + /// Stores the server assigned client file identifier in the associated + /// Realm file, such that it is available via get_status() during future + /// synchronization sessions. It is an error to set this identifier more + /// than once per Realm file. /// /// \param client_file_ident The server assigned client-side file /// identifier. A client-side file identifier is a non-zero positive integer @@ -109,13 +82,11 @@ class SyncHistory: /// identical identifiers for two client files if they are associated with /// different server Realms. /// - /// The client is required to obtain the file identifiers before engaging in - /// synchronization proper, and it must store the identifiers and use them - /// to reestablish the connection between the client file and the server - /// file when engaging in future synchronization sessions. - virtual void set_file_ident_pair(file_ident_type server_file_ident, - file_ident_type client_file_ident, - std::int_fast64_t client_file_ident_secret) = 0; + /// The client is required to obtain the file identifier before engaging in + /// synchronization proper, and it must store the identifier and use it to + /// reestablish the connection between the client file and the server file + /// when engaging in future synchronization sessions. + virtual void set_client_file_ident(SaltedFileIdent client_file_ident) = 0; /// Stores the SyncProgress progress in the associated Realm file in a way /// that makes it available via get_status() during future synchronization @@ -123,27 +94,15 @@ class SyncHistory: /// /// See struct SyncProgress for a description of \param progress. /// - /// `progress.scan_client_version` has an effect on the process by which old - /// history entries are discarded. - /// - /// `progress.scan_client_version` The version produced on this client by - /// the last changeset, that was sent to, and integrated by the server at - /// the time `progress.scan_server_version was produced, or zero if - /// `progress.scan_server_version` is zero. - /// - /// Since all changesets produced after `progress.scan_client_version` are - /// potentially needed during operational transformation of the next - /// changeset received from the server, the implementation of this class - /// must promise to retain all history entries produced after - /// `progress.scan_client_version`. That is, a history entry with a - /// changeset, that produces version V, is guaranteed to be retained as long - /// as V is strictly greater than `progress.scan_client_version`. - /// - /// It is an error to specify a client version that is less than the - /// currently stored version, since there is no way to get discarded history - /// back. - virtual void set_sync_progress(SyncProgress progress) = 0; + /// Note: The implementation is not obligated to store the value of + /// `progress.upload.last_integrated_server_version`, however, if the + /// implementation chooses to not store it, then its value will be + /// unreliable when passed to the implementation through functions such as + /// find_uploadable_changesets(). It may, or may not be the value last + /// returned for it by get_status(). + virtual void set_sync_progress(const SyncProgress& progress) = 0; +/* /// Get the first history entry whose changeset produced a version that /// succeeds `begin_version` and, and does not succeed `end_version`, whose /// changeset was not produced by integration of a changeset received from @@ -166,11 +125,67 @@ class SyncHistory: version_type end_version, HistoryEntry& entry, std::unique_ptr& buffer) const = 0; +*/ + + + struct UploadChangeset { + timestamp_type origin_timestamp; + file_ident_type origin_file_ident; + UploadCursor progress; + ChunkedBinaryData changeset; + std::unique_ptr buffer; + }; + + /// \brief Scan through the history for changesets to be uploaded. + /// + /// This function scans the history for changesets to be uploaded, i.e., for + /// changesets that are not empty, and were not produced by integration of + /// changesets recieved from the server. The scan begins at the position + /// specified by the initial value of \a upload_progress.client_version, and + /// ends no later than at the position specified by \a end_version. + /// + /// The implementation is allowed to end the scan before \a end_version, + /// such as to limit the combined size of returned changesets. However, if + /// the specified range contains any changesets that are supposed to be + /// uploaded, this function must return at least one. + /// + /// Upon return, \a upload_progress will have been updated to point to the + /// position from which the next scan should resume. This must be a position + /// after the last returned changeset, and before any remaining changesets + /// that are supposed to be uploaded, although never a position that + /// succeeds \a end_version. + /// + /// The value passed as \a upload_progress by the caller, must either be one + /// that was produced by an earlier invocation of + /// find_uploadable_changesets(), one that was returned by get_status(), or + /// one that was received by the client in a DOWNLOAD message from the + /// server. When the value comes from a DOWNLOAD message, it is supposed to + /// reflect a value of UploadChangeset::progress produced by an earlier + /// invocation of find_uploadable_changesets(). + /// + /// For changesets of local origin, UploadChangeset::origin_file_ident will + /// be zero. + virtual std::vector find_uploadable_changesets(UploadCursor& upload_progress, + version_type end_version) const = 0; using RemoteChangeset = Transformer::RemoteChangeset; - /// \brief Integrate a sequence of remote changesets using a single Realm - /// transaction. + // FIXME: Apparently, this feature is expected by object store, but why? + // What is it ultimately used for? (@tgoyne) + class SyncTransactReporter { + public: + virtual void report_sync_transact(VersionID old_version, VersionID new_version) = 0; + protected: + ~SyncTransactReporter() {} + }; + + enum class IntegrationError { + bad_origin_file_ident, + bad_changeset + }; + + /// \brief Integrate a sequence of changesets received from the server using + /// a single Realm transaction. /// /// Each changeset will be transformed as if by a call to /// Transformer::transform_remote_changeset(), and then applied to the @@ -179,28 +194,51 @@ class SyncHistory: /// As a final step, each changeset will be added to the local history (list /// of applied changesets). /// + /// This function checks whether the specified changesets specify valid + /// remote origin file identifiers and whether the changesets contain valid + /// sequences of instructions. The caller must already have ensured that the + /// origin file identifiers are strictly positive and not equal to the file + /// identifier assigned to this client by the server. + /// + /// If any of the changesets are invalid, this function returns false and + /// sets `integration_error` to the appropriate value. If they are all + /// deemed valid, this function sets `new_client_version` to the produced + /// client version. The type of version specified here, is the one that + /// identifies an entry in the sync history. Whether this is the same as the + /// snapshot number of the Realm file depends on the history implementation. + /// /// \param progress is the SyncProgress received in the download message. /// Progress will be persisted along with the changesets. /// /// \param num_changesets The number of passed changesets. Must be non-zero. /// - /// \param callback An optional callback which will be called with the + /// \param transact_reporter An optional callback which will be called with the /// version immediately processing the sync transaction and that of the sync /// transaction. - /// - /// \return The new local version produced by the application of the - /// transformed changeset. - virtual version_type integrate_remote_changesets(SyncProgress progress, - const RemoteChangeset* changesets, - std::size_t num_changesets, - util::Logger* replay_logger, - std::function& callback) = 0; + virtual bool integrate_server_changesets(const SyncProgress& progress, + const RemoteChangeset* changesets, + std::size_t num_changesets, + IntegrationError& integration_error, + version_type& new_client_version, util::Logger&, + SyncTransactReporter* transact_reporter = nullptr) = 0; + +protected: + ClientHistoryBase(const std::string& realm_path); +}; + + + +class ClientHistory : public ClientHistoryBase { +public: + class ChangesetCooker; + class Config; /// Get the persisted upload/download progress in bytes. - virtual void get_upload_download_bytes(uint_fast64_t& downloaded_bytes, - uint_fast64_t& downloadable_bytes, - uint_fast64_t& uploaded_bytes, - uint_fast64_t& uploadable_bytes) = 0; + virtual void get_upload_download_bytes(std::uint_fast64_t& downloaded_bytes, + std::uint_fast64_t& downloadable_bytes, + std::uint_fast64_t& uploaded_bytes, + std::uint_fast64_t& uploadable_bytes, + std::uint_fast64_t& snapshot_version) = 0; /// See set_cooked_progress(). struct CookedProgress { @@ -256,18 +294,18 @@ class SyncHistory: util::AppendBuffer&) const = 0; protected: - SyncHistory(const std::string& realm_path); + ClientHistory(const std::string& realm_path); }; /// \brief Abstract interface for changeset cookers. /// /// Note, it is completely up to the application to decide what a cooked -/// changeset is. History objects (instances of SyncHistory) are required to +/// changeset is. History objects (instances of ClientHistory) are required to /// treat cooked changesets as opaque entities. For an example of a concrete /// changeset cooker, see TrivialChangesetCooker which defines the cooked /// changesets to be identical copies of the raw changesets. -class SyncHistory::ChangesetCooker { +class ClientHistory::ChangesetCooker { public: /// \brief An opportunity to produce a cooked changeset. /// @@ -292,7 +330,7 @@ class SyncHistory::ChangesetCooker { }; -class SyncHistory::Config { +class ClientHistory::Config { public: Config() {} @@ -308,14 +346,14 @@ class SyncHistory::Config { /// If a changeset cooker is specified, then the created history object will /// allow for a cooked changeset to be produced for each changeset of remote /// origin; that is, for each changeset that is integrated during the - /// execution of SyncHistory::integrate_remote_changesets(). If no changeset - /// cooker is specified, then no cooked changesets will be produced on - /// behalf of the created history object. + /// execution of ClientHistory::integrate_remote_changesets(). If no + /// changeset cooker is specified, then no cooked changesets will be + /// produced on behalf of the created history object. /// - /// SyncHistory::integrate_remote_changesets() will pass each incoming + /// ClientHistory::integrate_remote_changesets() will pass each incoming /// changeset to the cooker after operational transformation; that is, when /// the chageset is ready to be applied to the local Realm state. - ChangesetCooker* changeset_cooker = nullptr; + std::shared_ptr changeset_cooker; }; /// \brief Create a "sync history" implementation of the realm::Replication @@ -323,15 +361,20 @@ class SyncHistory::Config { /// /// The intended role for such an object is as a plugin for new /// realm::SharedGroup objects. -std::unique_ptr make_sync_history(const std::string& realm_path, - SyncHistory::Config = {}); +std::unique_ptr make_client_history(const std::string& realm_path, + ClientHistory::Config = {}); // Implementation -inline SyncHistory::SyncHistory(const std::string& realm_path): - TrivialReplication(realm_path) +inline ClientHistoryBase::ClientHistoryBase(const std::string& realm_path): + InstructionReplication{realm_path} // Throws +{ +} + +inline ClientHistory::ClientHistory(const std::string& realm_path): + ClientHistoryBase{realm_path} // Throws { } diff --git a/Pods/Realm/include/core/realm/sync/instruction_applier.hpp b/Pods/Realm/include/core/realm/sync/instruction_applier.hpp new file mode 100644 index 0000000..2344c43 --- /dev/null +++ b/Pods/Realm/include/core/realm/sync/instruction_applier.hpp @@ -0,0 +1,125 @@ +/************************************************************************* + * + * Copyright 2017 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_SYNC_IMPL_INSTRUCTION_APPLIER_HPP +#define REALM_SYNC_IMPL_INSTRUCTION_APPLIER_HPP + +#include +#include +#include +#include + +namespace realm { +namespace sync { + +struct Changeset; + +struct InstructionApplier { + explicit InstructionApplier(Group& group) noexcept; + + /// Throws BadChangesetError if application fails due to a problem with the + /// changeset. + /// + /// FIXME: Consider using std::error_code instead of throwing + /// BadChangesetError. + void apply(const Changeset& log, util::Logger* logger); + + void begin_apply(const Changeset& log, util::Logger* logger) noexcept; + void end_apply() noexcept; + +protected: + StringData get_string(InternString) const; + StringData get_string(StringBufferRange) const; +#define REALM_DECLARE_INSTRUCTION_HANDLER(X) void operator()(const Instruction::X&); + REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DECLARE_INSTRUCTION_HANDLER) +#undef REALM_DECLARE_INSTRUCTION_HANDLER + friend struct Instruction; // to allow visitor + + template static void apply(A& applier, const Changeset& log, util::Logger* logger); + + Group& m_group; +private: + const Changeset* m_log = nullptr; + util::Logger* m_logger = nullptr; + TableRef m_selected_table; + TableRef m_selected_array; + LinkViewRef m_selected_link_list; + TableRef m_link_target_table; + TableInfoCache m_table_info; + + template + void log(const char* fmt, Args&&... args) + { + if (m_logger) { + m_logger->trace(fmt, std::forward(args)...); // Throws + } + } + + void bad_transaction_log(const char*) const; // Throws + + TableRef table_for_class_name(StringData) const; // Throws +}; + + + + +// Implementation + +inline InstructionApplier::InstructionApplier(Group& group) noexcept: + m_group(group), + m_table_info(m_group) +{ +} + +inline void InstructionApplier::begin_apply(const Changeset& log, util::Logger* logger) noexcept +{ + m_log = &log; + m_logger = logger; +} + +inline void InstructionApplier::end_apply() noexcept +{ + m_log = nullptr; + m_logger = nullptr; + m_selected_table = TableRef{}; + m_selected_link_list = LinkViewRef{}; + m_link_target_table = TableRef{}; +} + +template +inline void InstructionApplier::apply(A& applier, const Changeset& log, util::Logger* logger) +{ + applier.begin_apply(log, logger); + for (auto instr: log) { + if (!instr) + continue; + instr->visit(applier); // Throws + } + applier.end_apply(); + +} + +inline void InstructionApplier::apply(const Changeset& log, util::Logger* logger) +{ + apply(*this, log, logger); // Throws +} + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_IMPL_INSTRUCTION_APPLIER_HPP diff --git a/Pods/Realm/include/core/realm/sync/instruction_replication.hpp b/Pods/Realm/include/core/realm/sync/instruction_replication.hpp new file mode 100644 index 0000000..243bf21 --- /dev/null +++ b/Pods/Realm/include/core/realm/sync/instruction_replication.hpp @@ -0,0 +1,202 @@ +/************************************************************************* + * + * Copyright 2017 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_SYNC_IMPL_INSTRUCTION_REPLICATION_HPP +#define REALM_SYNC_IMPL_INSTRUCTION_REPLICATION_HPP + +#include +#include +#include +#include + +namespace realm { +namespace sync { + + +class InstructionReplication: public TrivialReplication, public ObjectIDProvider { +public: + explicit InstructionReplication(const std::string& realm_path); + void set_short_circuit(bool) noexcept; + bool is_short_circuited() const noexcept; + + // reset() resets the encoder, the selected tables and the cache. It is + // called by do_initiate_transact(), but can be called at the other times + // as well. + virtual void reset(); + + ChangesetEncoder& get_instruction_encoder() noexcept; + + //@{ + /// Generate instructions for Object Store tables. These must be called + /// prior to calling the equivalent functions in Core's API. When creating a + /// class-like table, `add_class()` must be called prior to + /// `Group::insert_group_level_table()`. Similarly, `create_object()` or + /// `create_object_with_primary_key()` must be called prior to + /// `Table::insert_empty_row()` and/or `Table::set_int_unique()` or + /// `Table::set_string_unique()` or `Table::set_null_unique()`. + /// + /// If a class-like table is added, or an object-like row is inserted, + /// without calling these methods first, an exception will be thrown. + /// + /// A "class-like table" is defined as a table whose name begins with + /// "class_" (this is the convention used by Object Store). Non-class-like + /// tables can be created and modified using Core's API without calling + /// these functions, because they do not result in instructions being + /// emitted. + void add_class(StringData table_name); + void add_class_with_primary_key(StringData table_name, DataType pk_type, StringData pk_field, bool nullable); + void create_object(const Table*, ObjectID); + void create_object_with_primary_key(const Table*, ObjectID, StringData); + void create_object_with_primary_key(const Table*, ObjectID, int_fast64_t); + void create_object_with_primary_key(const Table*, ObjectID, realm::util::None); + void prepare_erase_table(StringData table_name); + //@} + + // TrivialReplication interface: + void initialize(SharedGroup&) override; + + // TransactLogConvenientEncoder interface: + void insert_group_level_table(size_t table_ndx, size_t num_tables, StringData name) override; + void erase_group_level_table(size_t table_ndx, size_t num_tables) override; + void rename_group_level_table(size_t table_ndx, StringData new_name) override; + void insert_column(const Descriptor&, size_t col_ndx, DataType type, StringData name, LinkTargetInfo& link, + bool nullable = false) override; + void erase_column(const Descriptor&, size_t col_ndx) override; + void rename_column(const Descriptor&, size_t col_ndx, StringData name) override; + + void set_int(const Table*, size_t col_ndx, size_t ndx, int_fast64_t value, _impl::Instruction variant) override; + void add_int(const Table*, size_t col_ndx, size_t ndx, int_fast64_t value) override; + void set_bool(const Table*, size_t col_ndx, size_t ndx, bool value, _impl::Instruction variant) override; + void set_float(const Table*, size_t col_ndx, size_t ndx, float value, _impl::Instruction variant) override; + void set_double(const Table*, size_t col_ndx, size_t ndx, double value, _impl::Instruction variant) override; + void set_string(const Table*, size_t col_ndx, size_t ndx, StringData value, _impl::Instruction variant) override; + void set_binary(const Table*, size_t col_ndx, size_t ndx, BinaryData value, _impl::Instruction variant) override; + void set_olddatetime(const Table*, size_t col_ndx, size_t ndx, OldDateTime value, + _impl::Instruction variant) override; + void set_timestamp(const Table*, size_t col_ndx, size_t ndx, Timestamp value, _impl::Instruction variant) override; + void set_table(const Table*, size_t col_ndx, size_t ndx, _impl::Instruction variant) override; + void set_mixed(const Table*, size_t col_ndx, size_t ndx, const Mixed& value, _impl::Instruction variant) override; + void set_link(const Table*, size_t col_ndx, size_t ndx, size_t value, _impl::Instruction variant) override; + void set_null(const Table*, size_t col_ndx, size_t ndx, _impl::Instruction variant) override; + void set_link_list(const LinkView&, const IntegerColumn& values) override; + void insert_substring(const Table*, size_t col_ndx, size_t row_ndx, size_t pos, StringData) override; + void erase_substring(const Table*, size_t col_ndx, size_t row_ndx, size_t pos, size_t size) override; + void insert_empty_rows(const Table*, size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows) override; + void add_row_with_key(const Table*, size_t row_ndx, size_t prior_num_rows, size_t key_col_ndx, int64_t key) override; + void erase_rows(const Table*, size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rowsp, + bool is_move_last_over) override; + void swap_rows(const Table*, size_t row_ndx_1, size_t row_ndx_2) override; + void move_row(const Table*, size_t row_ndx_1, size_t row_ndx_2) override; + void merge_rows(const Table*, size_t row_ndx, size_t new_row_ndx) override; + void add_search_index(const Descriptor&, size_t col_ndx) override; + void remove_search_index(const Descriptor&, size_t col_ndx) override; + void set_link_type(const Table*, size_t col_ndx, LinkType) override; + void clear_table(const Table*, size_t prior_num_rows) override; + void optimize_table(const Table*) override; + void link_list_set(const LinkView&, size_t ndx, size_t value) override; + void link_list_insert(const LinkView&, size_t ndx, size_t value) override; + void link_list_move(const LinkView&, size_t from_ndx, size_t to_ndx) override; + void link_list_swap(const LinkView&, size_t ndx_1, size_t ndx_2) override; + void link_list_erase(const LinkView&, size_t ndx) override; + void link_list_clear(const LinkView&) override; + void nullify_link(const Table*, size_t col_ndx, size_t ndx) override; + void link_list_nullify(const LinkView&, size_t ndx) override; + +protected: + // Replication interface: + void do_initiate_transact(version_type current_version, bool history_updated) override; +private: + bool m_short_circuit = false; + + ChangesetEncoder m_encoder; + SharedGroup* m_sg = nullptr; + std::unique_ptr m_cache; + + enum class TableBehavior { + Class, + Array, + Ignore + }; + + // FIXME: The base class already caches this. + ConstTableRef m_selected_table; + TableBehavior m_selected_table_behavior; // cache + ConstLinkViewRef m_selected_link_list = nullptr; + + // Consistency checks: + std::string m_table_being_created; + std::string m_table_being_created_primary_key; + std::string m_table_being_erased; + util::Optional m_object_being_created; + + void unsupported_instruction(); // Throws TransformError + TableBehavior select_table(const Table*); + TableBehavior select_table(const Descriptor&); + bool select_link_list(const LinkView&); // returns true if table behavior != ignored + + TableBehavior get_table_behavior(const Table*) const; + + template + void set(const Table*, size_t row_ndx, size_t col_ndx, T payload, + _impl::Instruction variant); + template + void set_pk(const Table*, size_t row_ndx, size_t col_ndx, T payload, + _impl::Instruction variant); + template + auto as_payload(T value); + template + void emit(T instruction); +}; + +inline void InstructionReplication::set_short_circuit(bool b) noexcept +{ + m_short_circuit = b; +} + +inline bool InstructionReplication::is_short_circuited() const noexcept +{ + return m_short_circuit; +} + +inline ChangesetEncoder& InstructionReplication::get_instruction_encoder() noexcept +{ + return m_encoder; +} + +// Temporarily short-circuit replication +class TempShortCircuitReplication { +public: + TempShortCircuitReplication(InstructionReplication& bridge): m_bridge(bridge) + { + m_was_short_circuited = bridge.is_short_circuited(); + bridge.set_short_circuit(true); + } + + ~TempShortCircuitReplication() + { + m_bridge.set_short_circuit(m_was_short_circuited); + } +private: + InstructionReplication& m_bridge; + bool m_was_short_circuited; +}; + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_IMPL_INSTRUCTION_REPLICATION_HPP diff --git a/Pods/Realm/include/core/realm/sync/instructions.hpp b/Pods/Realm/include/core/realm/sync/instructions.hpp new file mode 100644 index 0000000..23ea945 --- /dev/null +++ b/Pods/Realm/include/core/realm/sync/instructions.hpp @@ -0,0 +1,398 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2015] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ + +#ifndef REALM_IMPL_INSTRUCTIONS_HPP +#define REALM_IMPL_INSTRUCTIONS_HPP + +#include +#include +#include // string conversion, debug prints +#include // shared_ptr +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace realm { +namespace sync { + +// CAUTION: Any change to the order or number of instructions is a +// protocol-breaking change! +#define REALM_FOR_EACH_INSTRUCTION_TYPE(X) \ + X(SelectTable) \ + X(SelectField) \ + X(AddTable) \ + X(EraseTable) \ + X(CreateObject) \ + X(EraseObject) \ + X(Set) \ + X(AddInteger) \ + X(InsertSubstring) \ + X(EraseSubstring) \ + X(ClearTable) \ + X(AddColumn) \ + X(EraseColumn) \ + X(ArraySet) \ + X(ArrayInsert) \ + X(ArrayMove) \ + X(ArraySwap) \ + X(ArrayErase) \ + X(ArrayClear) \ + + +enum class ContainerType { + None = 0, + Reserved0 = 1, + Array = 2, + Set = 3, + Dictionary = 4, +}; + +struct Instruction { + // Base classes for instructions with common fields. They enable the merge + // algorithm to reuse some code without resorting to templates, and can be + // combined to allow optimal memory layout of instructions (size <= 64). + struct PayloadInstructionBase; + struct ObjectInstructionBase; + struct FieldInstructionBase; + +#define REALM_DECLARE_INSTRUCTION_STRUCT(X) struct X; + REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DECLARE_INSTRUCTION_STRUCT) +#undef REALM_DECLARE_INSTRUCTION_STRUCT + + enum class Type: uint8_t { +#define REALM_DEFINE_INSTRUCTION_TYPE(X) X, + REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DEFINE_INSTRUCTION_TYPE) +#undef REALM_DEFINE_INSTRUCTION_TYPE + }; + + struct Payload; + template struct GetType; + template struct GetInstructionType; + + Instruction() {} + template + Instruction(T instr); + + static const size_t max_instruction_size = 64; + std::aligned_storage_t m_storage; + Type type; + + template + auto visit(F&& lambda); + template + auto visit(F&& lambda) const; + + template T& get_as() + { + REALM_ASSERT(type == GetInstructionType::value); + return *reinterpret_cast(&m_storage); + } + + template + const T& get_as() const + { + return const_cast(this)->template get_as(); + } +}; + +// 0x3f is the largest value that fits in a single byte in the variable-length +// encoded integer instruction format. +static constexpr uint8_t InstrTypeInternString = 0x3f; + +// This instruction code is only ever used internally by the Changeset class +// to allow insertion/removal while keeping iterators stable. Should never +// make it onto the wire. +static constexpr uint8_t InstrTypeMultiInstruction = 0xff; + +struct StringBufferRange { + uint32_t offset, size; + + bool operator==(const StringBufferRange&) = delete; + bool operator!=(const StringBufferRange&) = delete; +}; + +struct InternString { + static const InternString npos; + explicit constexpr InternString(uint32_t v = uint32_t(-1)): value(v) {} + + uint32_t value; + + // Disabling comparison for safety, because it is usually not what you want. + bool operator==(const InternString&) = delete; + bool operator!=(const InternString&) = delete; +}; + +struct Instruction::Payload { + struct Link { + sync::ObjectID target; // can be nothing = null + InternString target_table; + }; + + union Data { + bool boolean; + int64_t integer; + float fnum; + double dnum; + StringBufferRange str; + Timestamp timestamp; + Link link; + + Data() noexcept {} + Data(const Data&) noexcept = default; + Data& operator=(const Data&) noexcept = default; + }; + Data data; + int8_t type; // -1 = null, -2 = implicit_nullify + + Payload(): Payload(realm::util::none) {} + explicit Payload(bool value) noexcept: type(type_Bool) { data.boolean = value; } + explicit Payload(int64_t value) noexcept: type(type_Int) { data.integer = value; } + explicit Payload(float value) noexcept: type(type_Float) { data.fnum = value; } + explicit Payload(double value) noexcept: type(type_Double) { data.dnum = value; } + explicit Payload(Timestamp value) noexcept: type(type_Timestamp) { data.timestamp = value; } + explicit Payload(Link value) noexcept: type(type_Link) { data.link = value; } + explicit Payload(StringBufferRange value) noexcept: type(type_String) { data.str = value; } + explicit Payload(realm::util::None, bool implicit_null = false) noexcept { + type = (implicit_null ? -2 : -1); + } + + Payload(const Payload&) noexcept = default; + Payload& operator=(const Payload&) noexcept = default; + + bool is_null() const; + bool is_implicit_null() const; +}; + +struct Instruction::ObjectInstructionBase { + sync::ObjectID object; +}; + +struct Instruction::FieldInstructionBase + : Instruction::ObjectInstructionBase +{ + InternString field; +}; + +struct Instruction::PayloadInstructionBase { + Payload payload; +}; + + + +struct Instruction::SelectTable { + InternString table; +}; + +struct Instruction::SelectField + : Instruction::FieldInstructionBase +{ + InternString link_target_table; +}; + +struct Instruction::AddTable { + InternString table; + InternString primary_key_field; + DataType primary_key_type; + bool has_primary_key; + bool primary_key_nullable; +}; + +struct Instruction::EraseTable { + InternString table; +}; + +struct Instruction::CreateObject + : Instruction::PayloadInstructionBase + , Instruction::ObjectInstructionBase +{ + bool has_primary_key; +}; + +struct Instruction::EraseObject + : Instruction::ObjectInstructionBase +{}; + +struct Instruction::Set + : Instruction::PayloadInstructionBase + , Instruction::FieldInstructionBase +{ + bool is_default; +}; + +struct Instruction::AddInteger + : Instruction::FieldInstructionBase +{ + int64_t value; +}; + +struct Instruction::InsertSubstring + : Instruction::FieldInstructionBase +{ + StringBufferRange value; + uint32_t pos; +}; + +struct Instruction::EraseSubstring + : Instruction::FieldInstructionBase +{ + uint32_t pos; + uint32_t size; +}; + +struct Instruction::ClearTable { +}; + +struct Instruction::ArraySet { + Instruction::Payload payload; + uint32_t ndx; + uint32_t prior_size; +}; + +struct Instruction::ArrayInsert { + // payload carries the value in case of LinkList + // payload is empty in case of Array, Dict or any other container type + Instruction::Payload payload; + uint32_t ndx; + uint32_t prior_size; +}; + +struct Instruction::ArrayMove { + uint32_t ndx_1; + uint32_t ndx_2; +}; + +struct Instruction::ArrayErase { + uint32_t ndx; + uint32_t prior_size; + bool implicit_nullify; +}; + +struct Instruction::ArraySwap { + uint32_t ndx_1; + uint32_t ndx_2; +}; + +struct Instruction::ArrayClear { + uint32_t prior_size; +}; + + +// If container_type != ContainerType::none, creates a subtable: +// +---+---+-------+ +// | a | b | c | +// +---+---+-------+ +// | | | +---+ | +// | | | | v | | +// | | | +---+ | +// | 1 | 2 | | 3 | | +// | | | | 4 | | +// | | | | 5 | | +// | | | +---+ | +// +---+---+-------+ +struct Instruction::AddColumn { + InternString field; + InternString link_target_table; + DataType type; + ContainerType container_type; + bool nullable; +}; + +struct Instruction::EraseColumn { + InternString field; +}; + +struct InstructionHandler { + /// Notify the handler that an InternString meta-instruction was found. + virtual void set_intern_string(uint32_t index, StringBufferRange) = 0; + + /// Notify the handler of the string value. The handler guarantees that the + /// returned string range is valid at least until the next invocation of + /// add_string_range(). + /// + /// Instances of `StringBufferRange` passed to operator() after invoking + /// this function are assumed to refer to ranges in this buffer. + virtual StringBufferRange add_string_range(StringData) = 0; + + /// Handle an instruction. + virtual void operator()(const Instruction&) = 0; +}; + + +/// Implementation: + +#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4 // GCC 4.x does not support std::is_trivially_copyable +#define REALM_CHECK_TRIVIALLY_COPYABLE(X) static_assert(std::is_trivially_copyable::value, #X" Instructions must be trivially copyable."); + REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_CHECK_TRIVIALLY_COPYABLE) +#undef REALM_CHECK_TRIVIALLY_COPYABLE +#endif // __GNUC__ + +#ifdef _WIN32 // FIXME: Fails in VS. +#define REALM_CHECK_INSTRUCTION_SIZE(X) +#else +#define REALM_CHECK_INSTRUCTION_SIZE(X) static_assert(sizeof(Instruction::X) <= Instruction::max_instruction_size, #X" Instruction too big."); + REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_CHECK_INSTRUCTION_SIZE) +#undef REALM_CHECK_INSTRUCTION_SIZE +#endif + +#define REALM_DEFINE_INSTRUCTION_GET_TYPE(X) \ + template <> struct Instruction::GetType { using Type = Instruction::X; }; \ + template <> struct Instruction::GetInstructionType { static const Instruction::Type value = Instruction::Type::X; }; + REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DEFINE_INSTRUCTION_GET_TYPE) +#undef REALM_DEFINE_INSTRUCTION_GET_TYPE + + +template +Instruction::Instruction(T instr): type(GetInstructionType::value) +{ + new(&m_storage) T(std::move(instr)); +} + +template +auto Instruction::visit(F&& lambda) +{ + switch (type) { +#define REALM_VISIT_INSTRUCTION(X) \ + case Type::X: return lambda(get_as()); + REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_VISIT_INSTRUCTION) +#undef REALM_VISIT_INSTRUCTION + } + REALM_UNREACHABLE(); +} + +template +auto Instruction::visit(F&& lambda) const +{ + return const_cast(this)->visit(std::forward(lambda)); +} + +std::ostream& operator<<(std::ostream&, Instruction::Type); + +} // namespace _impl +} // namespace realm + +#endif // REALM_IMPL_INSTRUCTIONS_HPP diff --git a/Pods/Realm/include/core/realm/sync/metrics.hpp b/Pods/Realm/include/core/realm/sync/metrics.hpp deleted file mode 100644 index d4c5cfc..0000000 --- a/Pods/Realm/include/core/realm/sync/metrics.hpp +++ /dev/null @@ -1,94 +0,0 @@ -/************************************************************************* - * - * REALM CONFIDENTIAL - * __________________ - * - * [2011] - [2012] Realm Inc - * All Rights Reserved. - * - * NOTICE: All information contained herein is, and remains - * the property of Realm Incorporated and its suppliers, - * if any. The intellectual and technical concepts contained - * herein are proprietary to Realm Incorporated - * and its suppliers and may be covered by U.S. and Foreign Patents, - * patents in process, and are protected by trade secret or copyright law. - * Dissemination of this information or reproduction of this material - * is strictly forbidden unless prior written permission is obtained - * from Realm Incorporated. - * - **************************************************************************/ -#ifndef REALM_SYNC_METRICS_HPP -#define REALM_SYNC_METRICS_HPP - -#if REALM_HAVE_DOGLESS -# include -#endif - -namespace realm { -namespace sync { - -// FIXME: Consider adding support for specification of sample rate. The Dogless -// API already supports this. -class Metrics { -public: - /// Increment the counter identified by the specified key. - virtual void increment(const char* key) = 0; - - /// Set value of the guage identified by the specified key. - virtual void gauge(const char* key, double value) = 0; - - /// Add the specified value to the guage identified by the specified - /// key. The value is allowed to be negative. - virtual void gauge_relative(const char* key, double value) = 0; - - /// Allow the dogless library to send each metric to multiple endpoints, as - /// required - virtual void add_endpoint(const std::string& endpoint) = 0; - - virtual ~Metrics() {} -}; - -#if REALM_HAVE_DOGLESS - -class DoglessMetrics: public sync::Metrics { -public: - DoglessMetrics(): - m_dogless(dogless::hostname_prefix("realm")) // Throws - { - m_dogless.loop_interval(1); - } - - void increment(const char* key) override - { - const char* metric = key; - m_dogless.increment(metric); // Throws - } - - void gauge(const char* key, double value) override - { - const char* metric = key; - m_dogless.gauge(metric, value); // Throws - } - - void gauge_relative(const char* key, double value) override - { - const char* metric = key; - double amount = value; - m_dogless.gauge_relative(metric, amount); // Throws - } - - void add_endpoint(const std::string& endpoint) override - { - m_dogless.add_endpoint(endpoint); - } - -private: - dogless::BufferedStatsd m_dogless; -}; - -#endif - -} // namespace sync -} // namespace realm - -#endif // REALM_SYNC_METRICS_HPP diff --git a/Pods/Realm/include/core/realm/sync/object.hpp b/Pods/Realm/include/core/realm/sync/object.hpp new file mode 100644 index 0000000..347d372 --- /dev/null +++ b/Pods/Realm/include/core/realm/sync/object.hpp @@ -0,0 +1,250 @@ +/************************************************************************* + * + * Copyright 2017 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_SYNC_OBJECT_HPP +#define REALM_SYNC_OBJECT_HPP + +#include +#include +#include +#include + +#include + +#include + +/// This file presents a convenience API for making changes to a Realm file that +/// adhere to the conventions of assigning stable IDs to every object. + +namespace realm { + +class Group; + +namespace sync { + +class SyncHistory; + +static const char object_id_column_name[] = "!OID"; +static const char array_value_column_name[] = "!ARRAY_VALUE"; // FIXME call Jorgen + +struct TableInfoCache; + +/// Determine whether the Group has a sync-type history, and therefore whether +/// it supports globally stable object IDs. +/// +/// The Group does not need to be in a transaction. +bool has_object_ids(const Group&); + +/// Determine whether object IDs for objects without primary keys are globally +/// stable. This is true if and only if the Group has been in touch with the +/// server (or is the server), and will remain true forever thereafter. +/// +/// It is an error to call this function for groups that do not have object IDs +/// (i.e. where `has_object_ids()` returns false). +/// +/// The Group is assumed to be in a read transaction. +bool is_object_id_stability_achieved(const Group&); + +/// Create a table with an object ID column. +/// +/// It is an error to add tables to Groups with a sync history type directly. +/// This function or related functions must be used instead. +/// +/// The resulting table will be born with 1 column, which is a column used +/// in the maintenance of object IDs. +/// +/// NOTE: The table name must begin with the prefix "class_" in accordance with +/// Object Store conventions. +/// +/// The Group must be in a write transaction. +TableRef create_table(Group&, StringData name); + +/// Create a table with an object ID column and a primary key column. +/// +/// It is an error to add tables to Groups with a sync history type directly. +/// This function or related functions must be used instead. +/// +/// The resulting table will be born with 2 columns, which is a column used +/// in the maintenance of object IDs and the requested primary key column. +/// The primary key column must have either integer or string type, and it +/// will be given the name provided in the argument \a pk_column_name. +/// +/// The 'pk' metadata table is updated with information about the primary key +/// column. If the 'pk' table does not yet exist, it is created. +/// +/// Please note: The 'pk' metadata table will not be synchronized directly, +/// so subsequent updates to it will be lost (as they constitute schema-breaking +/// changes). +/// +/// NOTE: The table name must begin with the prefix "class_" in accordance with +/// Object Store conventions. +/// +/// The Group must be in a write transaction. +TableRef create_table_with_primary_key(Group&, StringData name, DataType pk_type, + StringData pk_column_name, bool nullable = false); + + +//@{ +/// Erase table and update metadata. +/// +/// It is an error to erase tables via the Group API, because it does not +/// correctly update metadata tables (such as the `pk` table). +void erase_table(Group&, StringData name); +void erase_table(Group&, TableRef); +//@} + +/// Create an array column with the specified element type. +/// +/// The result will be a column of type type_Table with one subcolumn named +/// "!ARRAY_VALUE" of the specified element type. +/// +/// Return the column index of the inserted array column. +size_t add_array_column(Table&, DataType element_type, StringData column_name); + + +//@{ +/// Calculate the object ID from the argument, where the argument is a primary +/// key value. +ObjectID object_id_for_primary_key(StringData); +ObjectID object_id_for_primary_key(util::Optional); +//@} + +/// Determine whether it is safe to call `object_id_for_row()` on tables without +/// primary keys. If the table has a primary key, always returns true. +bool has_globally_stable_object_ids(const Table&); + +bool table_has_primary_key(const TableInfoCache&, const Table&); + +/// Get the globally unique object ID for the row. +/// +/// If the table has a primary key, this is guaranteed to succeed. Otherwise, if +/// the server has not been contacted yet (`has_globally_stable_object_ids()` +/// returns false), an exception is thrown. +ObjectID object_id_for_row(const TableInfoCache&, const Table&, size_t); + +/// Get the index of the row with the object ID. +/// +/// \returns realm::npos if the object does not exist in the table. +size_t row_for_object_id(const TableInfoCache&, const Table&, ObjectID); + +//@{ +/// Add a row to the table and populate the object ID with an appropriate value. +/// +/// In the variant which takes an ObjectID parameter, a check is performed to see +/// if the object already exists. If it does, the row index of the existing object +/// is returned. +/// +/// If the table has a primary key column, an exception is thrown. +/// +/// \returns the row index of the object. +size_t create_object(const TableInfoCache&, Table&); +size_t create_object(const TableInfoCache&, Table&, ObjectID); +//@} + +//@{ +/// Create an object with a primary key value and populate the object ID with an +/// appropriate value. +/// +/// If the table does not have a primary key column (as indicated by the Object +/// Store's metadata in the special "pk" table), or the type of the primary key +/// column does not match the argument provided, an exception is thrown. +/// +/// The primary key column's value is populated with the appropriate +/// `set_int_unique()`, `set_string_unique()`, or `set_null_unique()` method +/// called on \a table. +/// +/// If an object with the given primary key value already exists, its row number +/// is returned without creating any new objects. +/// +/// These are convenience functions, equivalent to the following: +/// - Add an empty row to the table. +/// - Obtain an `ObjectID` with `object_id_for_primary_key()`. +/// - Obtain a local object ID with `global_to_local_object_id()`. +/// - Store the local object ID in the object ID column. +/// - Call `set_int_unique()`,`set_string_unique()`, or `set_null_unique()` +/// to set the primary key value. +/// +/// \returns the row index of the created object. +size_t create_object_with_primary_key(const TableInfoCache&, Table&, util::Optional primary_key); +size_t create_object_with_primary_key(const TableInfoCache&, Table&, StringData primary_key); +//@} + +struct TableInfoCache { + const Group& m_group; + + // Implicit conversion deliberately allowed for the purpose of calling the above + // functions without constructing a cache manually. + TableInfoCache(const Group&); + TableInfoCache(TableInfoCache&&) noexcept = default; + + struct TableInfo { + struct VTable; + + StringData name; + const VTable* vtable; + size_t object_id_index; + size_t primary_key_index; + DataType primary_key_type = DataType(-1); + bool primary_key_nullable = false; + mutable size_t last_row_index = size_t(-1); + mutable ObjectID last_object_id; + }; + + mutable std::vector> m_table_info; + + const TableInfo& get_table_info(const Table&) const; + const TableInfo& get_table_info(size_t table_index) const; + void clear(); + void verify(); +}; + + +/// Migrate a server-side Realm file whose history type is +/// `Replication::hist_SyncServer` and whose history schema version is 0 (i.e., +/// Realm files without stable identifiers). +void import_from_legacy_format(const Group& old_group, Group& new_group, util::Logger&); + +using TableNameBuffer = std::array; +StringData table_name_to_class_name(StringData); +StringData class_name_to_table_name(StringData, TableNameBuffer&); + + +// Implementation: + +inline StringData table_name_to_class_name(StringData table_name) +{ + REALM_ASSERT(table_name.begins_with("class_")); + return table_name.substr(6); +} + + +inline StringData class_name_to_table_name(StringData class_name, TableNameBuffer& buffer) +{ + constexpr const char class_prefix[] = "class_"; + constexpr size_t class_prefix_len = sizeof(class_prefix) - 1; + char* p = std::copy_n(class_prefix, class_prefix_len, buffer.data()); + size_t len = std::min(class_name.size(), buffer.size() - class_prefix_len); + std::copy_n(class_name.data(), len, p); + return StringData(buffer.data(), class_prefix_len + len); +} + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_OBJECT_HPP + diff --git a/Pods/Realm/include/core/realm/sync/object_id.hpp b/Pods/Realm/include/core/realm/sync/object_id.hpp new file mode 100644 index 0000000..c0028b8 --- /dev/null +++ b/Pods/Realm/include/core/realm/sync/object_id.hpp @@ -0,0 +1,287 @@ +/************************************************************************* + * + * Copyright 2017 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_SYNC_OBJECT_ID_HPP +#define REALM_SYNC_OBJECT_ID_HPP + +#include // std::hash +#include +#include // operator<< +#include +#include + +#include + +#include +#include +#include + +// Only set this to one when testing the code paths that exercise object ID +// hash collisions. It artificially limits the "optimistic" local ID to use +// only the lower 15 bits of the ID rather than the lower 63 bits, making it +// feasible to generate collisions within reasonable time. +#define REALM_EXERCISE_OBJECT_ID_COLLISION 0 + +namespace realm { + +class Group; + +namespace sync { + +/// ObjectIDs are globally unique, and up to 128 bits wide. They are represented +/// as two 64-bit integers, each of which may frequently be small, for best +/// on-wire compressibility. +struct ObjectID { + constexpr ObjectID(uint64_t hi, uint64_t lo); + static ObjectID from_string(StringData); + + // FIXME: Remove "empty" ObjectIDs, wrap in Optional instead. + constexpr ObjectID(realm::util::None = realm::util::none); + constexpr ObjectID(const ObjectID&) noexcept = default; + ObjectID& operator=(const ObjectID&) noexcept = default; + + constexpr uint64_t lo() const { return m_lo; } + constexpr uint64_t hi() const { return m_hi; } + + std::string to_string() const; + + constexpr bool operator<(const ObjectID& other) const; + constexpr bool operator==(const ObjectID& other) const; + constexpr bool operator!=(const ObjectID& other) const; + +private: + uint64_t m_lo; + uint64_t m_hi; +}; + +/// Implementors of this interface should define a way to map from 128-bit +/// on-write ObjectIDs to local 64-bit object IDs. +/// +/// The three object ID types are: +/// a. Object IDs for objects in tables without primary keys. +/// b. Object IDs for objects in tables with integer primary keys. +/// c. Object IDs for objects in tables with other primary key types. +/// +/// For integer primary keys (b), the Object ID is just the integer value. +/// +/// For objects without primary keys (a), a "squeezed" tuple of the +/// client_file_ident and a peer-local sequence number is used as the local +/// Object ID. The on-write Object ID is the "unsqueezed" format. The methods on +/// this interface ending in "_squeezed" aid in the creation and conversion of +/// these IDs. +/// +/// For objects with other types of primary keys (c), the ObjectID +/// is a 128-bit hash of the primary key value. However, the local object ID +/// must be a 64-bit integer, because that is the maximum size integer that +/// Realm is able to store. The solution is to optimistically use the lower 63 +/// bits of the on-wire Object ID, and use a local ID with the upper 64th bit +/// set when there is a collision in the lower 63 bits between two different +/// hash values. +class ObjectIDProvider { +public: + using LocalObjectID = int_fast64_t; + + /// Calculate optimistic local ID that may collide with others. It is up to + /// the caller to ensure that collisions are detected and that + /// allocate_local_id_after_collision() is called to obtain a non-colliding + /// ID. + static LocalObjectID get_optimistic_local_id_hashed(ObjectID global_id); + + /// Find the local 64-bit object ID for the provided global 128-bit ID. + virtual LocalObjectID global_to_local_object_id_hashed(size_t table_ndx, ObjectID global_id) const = 0; + + /// After a local ID collision has been detected, this function may be + /// called to obtain a non-colliding local ID in such a way that subsequence + /// calls to global_to_local_object_id() will return the correct local ID + /// for both \a incoming_id and \a colliding_id. + virtual LocalObjectID allocate_local_id_after_hash_collision(size_t table_ndx, + ObjectID incoming_id, + ObjectID colliding_id, + LocalObjectID colliding_local_id) = 0; + static LocalObjectID make_tagged_local_id_after_hash_collision(uint64_t sequence_number); + virtual void free_local_id_after_hash_collision(size_t table_ndx, ObjectID object_id) = 0; + + /// Some Object IDs are generated as a tuple of the client_file_ident and a + /// local sequence number. This function takes the next number in the + /// sequence for the given table and returns an appropriate globally unique + /// ObjectID. + virtual ObjectID allocate_object_id_squeezed(size_t table_ndx) = 0; + static LocalObjectID global_to_local_object_id_squeezed(ObjectID); + static ObjectID local_to_global_object_id_squeezed(LocalObjectID); + + virtual void table_erased(size_t table_ndx) = 0; + + virtual int_fast64_t get_client_file_ident() const = 0; +}; + +// ObjectIDSet is a set of (table name, object id) +class ObjectIDSet { +public: + + void insert(StringData table, ObjectID object_id); + void erase(StringData table, ObjectID object_id); + bool contains(StringData table, ObjectID object_id) const noexcept; + + // A map from table name to a set of object ids. + std::map, std::less<>> m_objects; +}; + +// FieldSet is a set of fields in tables. A field is defined by a +// table name, a column in the table and an object id for the row. +class FieldSet { +public: + + void insert(StringData table, StringData column, ObjectID object_id); + void erase(StringData table, StringData column, ObjectID object_id); + bool contains(StringData table, ObjectID object_id) const noexcept; + bool contains(StringData table, StringData column, ObjectID object_id) const noexcept; + + // A map from table name to a map from column name to a set of + // object ids. + std::map< + std::string, + std::map, std::less<>>, + std::less<> + > m_fields; +}; + +struct GlobalID { + StringData table_name; + ObjectID object_id; + + bool operator==(const GlobalID& other) const; + bool operator!=(const GlobalID& other) const; + bool operator<(const GlobalID& other) const; +}; + +/// Implementation: + + +constexpr ObjectID::ObjectID(uint64_t hi, uint64_t lo): m_lo(lo), m_hi(hi) +{ +} + +constexpr ObjectID::ObjectID(realm::util::None): m_lo(-1), m_hi(-1) +{ +} + +constexpr bool ObjectID::operator<(const ObjectID& other) const +{ + return (m_hi == other.m_hi) ? (m_lo < other.m_lo) : (m_hi < other.m_hi); +} + +constexpr bool ObjectID::operator==(const ObjectID& other) const +{ + return m_hi == other.m_hi && m_lo == other.m_lo; +} + +constexpr bool ObjectID::operator!=(const ObjectID& other) const +{ + return !(*this == other); +} + +inline bool GlobalID::operator==(const GlobalID& other) const +{ + return table_name == other.table_name && object_id == other.object_id; +} + +inline bool GlobalID::operator!=(const GlobalID& other) const +{ + return !(*this == other); +} + +inline bool GlobalID::operator<(const GlobalID& other) const +{ + if (table_name == other.table_name) + return object_id < other.object_id; + return table_name < other.table_name; +} + + +std::ostream& operator<<(std::ostream&, const realm::sync::ObjectID&); + +inline ObjectIDProvider::LocalObjectID +ObjectIDProvider::get_optimistic_local_id_hashed(ObjectID global_id) +{ +#if REALM_EXERCISE_OBJECT_ID_COLLISION + const uint64_t optimistic_mask = 0xff; +#else + const uint64_t optimistic_mask = 0x7fffffffffffffff; +#endif + static_assert(optimistic_mask < 0x8000000000000000, "optimistic Object ID mask must leave the 64th bit zero"); + return global_id.lo() & optimistic_mask; +} + +inline ObjectIDProvider::LocalObjectID +ObjectIDProvider::make_tagged_local_id_after_hash_collision(uint64_t sequence_number) +{ + REALM_ASSERT(sequence_number < 0x8000000000000000); + return 0x8000000000000000 | sequence_number; +} + +inline ObjectIDProvider::LocalObjectID +ObjectIDProvider::global_to_local_object_id_squeezed(ObjectID object_id) +{ + REALM_ASSERT(object_id.hi() <= std::numeric_limits::max()); + REALM_ASSERT(object_id.lo() <= std::numeric_limits::max()); + + uint64_t a = object_id.lo() & 0xff; + uint64_t b = (object_id.hi() & 0xff) << 8; + uint64_t c = (object_id.lo() & 0xffffff00) << 8; + uint64_t d = (object_id.hi() & 0xffffff00) << 32; + union { + uint64_t u; + int64_t s; + } bitcast; + bitcast.u = a | b | c | d; + return bitcast.s; +} + +inline ObjectID +ObjectIDProvider::local_to_global_object_id_squeezed(LocalObjectID squeezed) +{ + union { + uint64_t u; + int64_t s; + } bitcast; + bitcast.s = squeezed; + + uint64_t u = bitcast.u; + + uint64_t lo = (u & 0xff) | ((u & 0xffffff0000) >> 8); + uint64_t hi = ((u & 0xff00) >> 8) | ((u & 0xffffff0000000000) >> 32); + return ObjectID{hi, lo}; +} + +} // namespace sync +} // namespace realm + +namespace std { + +template <> +struct hash { + size_t operator()(realm::sync::ObjectID oid) const + { + return std::hash{}(oid.lo()) ^ std::hash{}(oid.hi()); + } +}; + +} // namespace std + +#endif // REALM_SYNC_OBJECT_ID_HPP + diff --git a/Pods/Realm/include/core/realm/sync/permissions.hpp b/Pods/Realm/include/core/realm/sync/permissions.hpp new file mode 100644 index 0000000..5c393a5 --- /dev/null +++ b/Pods/Realm/include/core/realm/sync/permissions.hpp @@ -0,0 +1,398 @@ + +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2015] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ + +#ifndef REALM_SYNC_PERMISSIONS_HPP +#define REALM_SYNC_PERMISSIONS_HPP + +#include + +#include +#include +#include + +#include + +namespace realm { +namespace sync { + +/// Permissions Schema: +/// +/// class___Role: +/// string name PRIMARY_KEY; +/// User[] members; +/// +/// class___Permission: +/// __Role role; +/// bool canRead; +/// bool canUpdate; +/// bool canDelete; +/// bool canSetPermissions; +/// bool canQuery; +/// bool canCreate; +/// bool canModifySchema; +/// +/// class___Realm: +/// int id PRIMARY_KEY = 0; // singleton object +/// __Permission[] permissions; +/// +/// class___User: +/// string id PRIMARY_KEY; +/// __Role role; +/// +/// class___Class: +/// string name PRIMARY_KEY; +/// __Permission[] permissions; +/// +/// class_: +/// __Permission[] ; +/// __Role ; +/// + +static constexpr char g_roles_table_name[] = "class___Role"; +static constexpr char g_permissions_table_name[] = "class___Permission"; +static constexpr char g_users_table_name[] = "class___User"; +static constexpr char g_classes_table_name[] = "class___Class"; +static constexpr char g_realms_table_name[] = "class___Realm"; + + +/// Create the permissions schema if it doesn't already exist. +void create_permissions_schema(Group&); + +/// Set up the basic "everyone" role and default permissions. The default is to +/// set up some very permissive defaults, where "everyone" can do everything. +void set_up_basic_permissions(Group&, bool permissive = true); + +void set_up_basic_permissions_for_class(Group&, StringData class_name, bool permissive = true); + +/// Set up some basic permissions for the class. The default is to set up some +/// very permissive default, where "everyone" can do everything in the class. +// void set_up_basic_default_permissions_for_class(Group&, TableRef klass, bool permissive = true); + +/// Return the index of the ACL in the class, if one exists. If no ACL column is +/// defined in the class, returns `npos`. +size_t find_permissions_column(const Group&, ConstTableRef); + +//@{ +/// Convenience functions to check permisions data +/// The functions must be called inside a read (or write) transaction. +bool permissions_schema_exist(const Group&); + +bool user_exist(const Group&, StringData user_id); +//@} + +//@{ +/// Convenience function to modify permission data. +/// +/// When a role or user has not already been defined in the Realm, these +/// functions create them on-demand. +void set_realm_permissions_for_role(Group&, StringData role_name, + uint_least32_t privileges); +void set_class_permissions_for_role(Group&, StringData class_name, + StringData role_name, uint_least32_t privileges); +// void set_default_object_permissions_for_role(Group&, StringData class_name, +// StringData role_name, +// uint_least32_t privileges); +void set_object_permissions_for_role(Group&, TableRef table, size_t row_ndx, + StringData role_name, uint_least32_t privileges); + +void add_user_to_role(Group&, StringData user_id, StringData role_name); +//@} + +/// The Privilege enum is intended to be used in a bitfield. +enum class Privilege : uint_least32_t { + None = 0, + + /// The user can read the object (i.e. it can participate in the user's + /// subscription. + /// + /// NOTE: On objects, it is a prerequisite that the object's class is also + /// readable by the user. + /// + /// FIXME: Until we get asynchronous links, any object that is reachable + /// through links from another readable/queryable object is also readable, + /// regardless of whether the user specifically does not have read access. + Read = 1, + + /// The user can modify the fields of the object. + /// + /// NOTE: On objects, it is a prerequisite that the object's class is also + /// updatable by the user. When applied to a Class object, it does not + /// imply that the user can modify the schema of the class, only the + /// objects of that class. + /// + /// NOTE: This does not imply the SetPermissions privilege. + Update = 2, + + /// The user can delete the object. + /// + /// NOTE: When applied to a Class object, it has no effect on whether + /// objects of that class can be deleted by the user. + /// + /// NOTE: This implies the ability to implicitly nullify links pointing + /// to the object from other objects, even if the user does not have + /// permission to modify those objects in the normal way. + Delete = 4, + + //@{ + /// The user can modify the object's permissions. + /// + /// NOTE: The user will only be allowed to assign permissions at or below + /// their own privilege level. + SetPermissions = 8, + Share = SetPermissions, + //@} + + /// When applied to a Class object, the user can query objects in that + /// class. + /// + /// Has no effect when applied to objects other than Class. + Query = 16, + + /// When applied to a Class object, the user may create objects in that + /// class. + /// + /// NOTE: The user implicitly has Update and SetPermissions + /// (but not necessarily Delete permission) within the same + /// transaction as the object was created. + /// + /// NOTE: Even when a user has CreateObject rights, a CreateObject + /// operation may still be rejected by the server, if the object has a + /// primary key and the object already exists, but is not accessible by the + /// user. + Create = 32, + + /// When applied as a "Realm" privilege, the user can add classes and add + /// columns to classes. + /// + /// NOTE: When applied to a class or object, this has no effect. + ModifySchema = 64, + + /// + /// Aggregate permissions for compatibility: + /// + Download = Read | Query, + Upload = Update | Delete | Create, + DeleteRealm = Upload, // FIXME: This seems overly permissive +}; + +inline constexpr uint_least32_t operator|(Privilege a, Privilege b) +{ + return static_cast(a) | static_cast(b); +} + +inline constexpr uint_least32_t operator|(uint_least32_t a, Privilege b) +{ + return a | static_cast(b); +} + +inline constexpr uint_least32_t operator&(Privilege a, Privilege b) +{ + return static_cast(a) & static_cast(b); +} + +inline constexpr uint_least32_t operator&(uint_least32_t a, Privilege b) +{ + return a & static_cast(b); +} + +inline uint_least32_t& operator|=(uint_least32_t& a, Privilege b) +{ + return a |= static_cast(b); +} + +inline constexpr uint_least32_t operator~(Privilege p) +{ + return ~static_cast(p); +} + +struct PermissionsCache { + PermissionsCache(const Group& g, StringData user_identity, bool is_admin = false); + + + bool is_admin() const noexcept; + + /// Get Realm-level privileges for the current user. + /// + /// The user must have Read access at the Realm level to be able to see + /// anything in the file. + /// + /// The user must have Update access at the Realm level to be able to make + /// any changes at all in the Realm file. + /// + /// If no Realm-level permissions are defined, no access is granted for any + /// user. + uint_least32_t get_realm_privileges(); + + /// Get class-level privileges for the current user and the given class. + /// + /// If the class does not have any class-level privileges defined, no access + /// is granted to the class. + /// + /// Calling this function is equivalent to calling `get_object_privileges()` + /// with an object of the type `__Class`. + /// + /// NOTE: This function only considers class-level permissions. It does not + /// mask the returned value by the Realm-level permissions. See `can()`. + uint_least32_t get_class_privileges(StringData class_name); + + /// Get object-level privileges for the current user and the given object. + /// + /// If the object's class has an ACL property (a linklist to the + /// `__Permission` class), and it isn't empty, the user's privileges is the + /// OR'ed privileges for the intersection of roles that have a defined + /// permission on the object and the roles of which the user is a member. + /// + /// If the object's ACL property is empty (but the column exists), no access + /// is granted to anyone. + /// + /// If the object does not exist in the table, the returned value is + /// equivalent to that of an object with an empty ACL property, i.e. no + /// privileges are granted. Note that the existence of the column is checked + /// first, so an absent ACL property (granting all privileges) takes + /// precedence over an absent object (granting no privileges) in terms of + /// calculating permissions. + /// + /// NOTE: This function only considers object-level permissions (per-object + /// ACLs or default object permissions). It does not mask the returned value + /// by the object's class-level permissions, or by the Realm-level + /// permissions. See `can()`. + uint_least32_t get_object_privileges(GlobalID); + + /// Get object-level privileges without adding it to the cache. + uint_least32_t get_object_privileges_nocache(GlobalID); + + //@{ + /// Check permissions for the object, taking all levels of permission into + /// account. + /// + /// This method only returns `true` if the user has Realm-level access to + /// the object, class-level access to the object, and object-level access to + /// the object. + /// + /// In the version where the first argument is a mask of privileges, the + /// method only returns `true` when all privileges are satisfied. + bool can(Privilege privilege, GlobalID object_id); + bool can(uint_least32_t privileges, GlobalID object_id); + //@} + + /// Invalidate all cache entries pertaining to the object. + /// + /// The object may be an instance of `__Class`. + void object_permissions_modified(GlobalID); + + /// Register the object as created in this transaction, meaning that the + /// user gets full privileges until the end of the transaction. + void object_created(GlobalID); + + /// Invalidate all cache entries pertaining to the class. + // void default_object_permissions_modified(StringData class_name); + + /// Invalidate all cached permissions. + void clear(); + + /// Check that all cache permissions correspond to the current permission + /// state in the database. + void verify(); + +private: + const Group& group; + TableInfoCache cache; + std::string user_id; + bool m_is_admin; + util::Optional realm_privileges; + std::map object_privileges; + ObjectIDSet created_objects; + + // uint_least32_t get_default_object_privileges(ConstTableRef); + uint_least32_t get_privileges_for_permissions(ConstLinkViewRef); + friend struct InstructionApplierWithPermissionCheck; +}; + +inline bool PermissionsCache::is_admin() const noexcept +{ + return m_is_admin; +} + +/// PermissionCorrections is a struct that describes some changes that must be +/// sent to the client because the client tried to perform changes to a database +/// that it wasn't allowed to make. +struct PermissionCorrections { + using TableColumnSet = std::map>, std::less<>>; + using TableSet = std::set>; + + // Objects that a client tried to delete without being allowed. + ObjectIDSet recreate_objects; + + // Objects that a client tried to create without being allowed. + ObjectIDSet erase_objects; + + // Fields that were illegally modified by the client and must be reset. + // + // Objects mentioned in `recreate_objects` and `erase_objects` are not + // mentioned here. + FieldSet reset_fields; + + // Columns that were illegally added by the client. + TableColumnSet erase_columns; + + // Columns that were illegally removed by the client. + TableColumnSet recreate_columns; + + // Tables that were illegally added by the client. + // std::set erase_tables; + TableSet erase_tables; + + // Tables that were illegally removed by the client. + TableSet recreate_tables; +}; + +// Function for printing out a permission correction object. Useful for debugging purposes. +std::ostream& operator<<(std::ostream&, const PermissionCorrections&); + + + +/// InstructionApplierWithPermissionCheck conditionally applies each +/// instruction, and builds a `PermissionCorrections` struct based on the +/// illicit changes. The member `m_corrections` can be used to synthesize a +/// changeset that can be sent to the client to revert the illicit changes that +/// were detected by the applier. +struct InstructionApplierWithPermissionCheck { + explicit InstructionApplierWithPermissionCheck(Group& reference_realm, + bool is_admin, + StringData user_identity); + ~InstructionApplierWithPermissionCheck(); + + /// Apply \a incoming_changeset, checking permissions in the process. + /// Populates `m_corrections`. + void apply(const Changeset& incoming_changeset, util::Logger*); + + PermissionCorrections m_corrections; + +private: + struct Impl; + std::unique_ptr m_impl; +}; + +} // namespace sync +} // namespace realm + + +#endif // REALM_SYNC_PERMISSIONS_HPP diff --git a/Pods/Realm/include/core/realm/sync/protocol.hpp b/Pods/Realm/include/core/realm/sync/protocol.hpp index 6597129..d823cef 100644 --- a/Pods/Realm/include/core/realm/sync/protocol.hpp +++ b/Pods/Realm/include/core/realm/sync/protocol.hpp @@ -20,18 +20,11 @@ #ifndef REALM_SYNC_PROTOCOL_HPP #define REALM_SYNC_PROTOCOL_HPP +#include #include -#include -#include -#include -#include -#include +#include -#include -#include - -#include // Get rid of this? // NOTE: The protocol specification is in `/doc/protocol.md` @@ -78,24 +71,138 @@ namespace sync { // // 9 New format of the DOWNLOAD message to support progress reporting on the // client +// // 10 Error codes reordered (now categorized as either connection or session // level errors). +// // 11 Bugfixes in Link List and ChangeLinkTargets merge rules, that -// make previous versions incompatible. +// make previous versions incompatible. +// // 12 FIXME What was 12? +// // 13 Bugfixes in Link List and ChangeLinkTargets merge rules, that // make previous versions incompatible. +// // 14 Further bugfixes related to primary keys and link lists. Add support for // LinkListSwap. +// // 15 Deleting an object with a primary key deletes all objects on other // with the same primary key. +// // 16 Downloadable bytes added to DOWNLOAD message. It is used for download progress // by the client +// +// 17 Added PING and PONG messages. It is used for rtt monitoring and dead +// connection detection by both the client and the server. +// +// 18 Enhanced the session_ident to accept values of size up to at least 63 bits. +// +// 19 New instruction log format with stable object IDs and arrays of +// primitives (Generalized LinkList* commands to Container* commands) +// Message format is identical to version 18. +// +// 20 Added support for log compaction in DOWNLOAD message. +// +// 21 Removed "class_" prefix in instructions referencing tables. +// +// 22 Fixed a bug in the merge rule of MOVE vs SWAP. +// +// 23 Introduced full support for session specific ERROR messages. Removed the +// obsolete concept of a "server file identifier". Added support for relayed +// subtier client file identifier allocation. For this purpose, the message +// that was formerly known as ALLOC was renamed to IDENT, and a new ALLOC +// message was added in both directions. Added the ability for an UPLOAD +// message to carry a per-changeset origin client file identifier. Added +// `` parameter to DOWNLOAD message. Added new error +// codes 215 "Unsupported session-level feature" and 216 "Bad origin client +// file identifier (UPLOAD)". +// +// 24 Support schema-breaking instructions. Official support for partial sync. + constexpr int get_current_protocol_version() noexcept { - return 16; + return 24; } + +// These integer types are selected so that they accomodate the requirements of +// the protocol specification (`/doc/protocol.md`). +using file_ident_type = std::uint_fast64_t; +using version_type = Replication::version_type; +using salt_type = std::int_fast64_t; +using timestamp_type = std::uint_fast64_t; +using session_ident_type = std::uint_fast64_t; +using request_ident_type = std::uint_fast64_t; +using milliseconds_type = std::int_fast64_t; + +constexpr file_ident_type get_max_file_ident() +{ + return 0x0'7FFF'FFFF'FFFF'FFFF; +} + + +struct SaltedFileIdent { + file_ident_type ident; + /// History divergence and identity spoofing protection. + salt_type salt; +}; + +struct SaltedVersion { + version_type version; + /// History divergence protection. + salt_type salt; +}; + + +/// \brief A client's reference to a position in the server-side history. +/// +/// A download cursor refers to a position in the server-side history. If +/// `server_version` is zero, the position is at the beginning of the history, +/// otherwise the position is after the entry whose changeset produced that +/// version. In general, positions are to be understood as places between two +/// adjacent history entries. +/// +/// `last_integrated_client_version` is the version produced on the client by +/// the last changeset that was sent to the server and integrated into the +/// server-side Realm state at the time indicated by the history position +/// specified by `server_version`, or zero if no changesets from the client were +/// integrated by the server at that point in time. +struct DownloadCursor { + version_type server_version; + version_type last_integrated_client_version; +}; + + +/// \brief The server's reference to a position in the client-side history. +/// +/// An upload cursor refers to a position in the client-side history. If +/// `client_version` is zero, the position is at the beginning of the history, +/// otherwise the position is after the entry whose changeset produced that +/// version. In general, positions are to be understood as places between two +/// adjacent history entries. +/// +/// `last_integrated_server_version` is the version produced on the server by +/// the last changeset that was sent to the client and integrated into the +/// client-side Realm state at the time indicated by the history position +/// specified by `client_version`, or zero if no changesets from the server were +/// integrated by the client at that point in time. +struct UploadCursor { + version_type client_version; + version_type last_integrated_server_version; +}; + + +/// A client's record of the current point of progress of the synchronization +/// process. The client must store this persistently in the local Realm file. +struct SyncProgress { + SaltedVersion latest_server_version{0, 0}; + DownloadCursor download{0, 0}; + UploadCursor upload{0, 0}; + std::int_fast64_t downloadable_bytes = 0; +}; + + + /// \brief Protocol errors discovered by the server, and reported to the client /// by way of ERROR messages. /// @@ -116,6 +223,10 @@ enum class ProtocolError { reuse_of_session_ident = 107, // Overlapping reuse of session identifier (BIND) bound_in_other_session = 108, // Client file bound in other session (IDENT) bad_message_order = 109, // Bad input message order + bad_decompression = 110, // Error in decompression (UPLOAD) + bad_changeset_header_syntax = 111, // Bad syntax in a changeset header (UPLOAD) + bad_changeset_size = 112, // Bad size specified in changeset header (UPLOAD) + bad_changesets = 113, // Bad changesets (UPLOAD) // Session level errors session_closed = 200, // Session closed (no error) @@ -125,13 +236,17 @@ enum class ProtocolError { illegal_realm_path = 204, // Illegal Realm path (BIND) no_such_realm = 205, // No such Realm (BIND) permission_denied = 206, // Permission denied (BIND, REFRESH) - bad_server_file_ident = 207, // Bad server file identifier (IDENT) + bad_server_file_ident = 207, // Bad server file identifier (IDENT) (obsolete!) bad_client_file_ident = 208, // Bad client file identifier (IDENT) bad_server_version = 209, // Bad server version (IDENT, UPLOAD) bad_client_version = 210, // Bad client version (IDENT, UPLOAD) diverging_histories = 211, // Diverging histories (IDENT) bad_changeset = 212, // Bad changeset (UPLOAD) - disabled_session = 213, // Disabled session + superseded = 213, // Superseded by new session for same client-side file (deprecated) + disabled_session = 213, // Alias for `superseded` (deprecated) + partial_sync_disabled = 214, // Partial sync disabled (BIND) + unsupported_session_feature = 215, // Unsupported session-level feature + bad_origin_file_ident = 216, // Bad origin file identifier (UPLOAD) }; inline constexpr bool is_session_level_error(ProtocolError error) @@ -160,551 +275,7 @@ template<> struct is_error_code_enum { namespace realm { namespace sync { -namespace protocol { - -using OutputBuffer = util::ResettableExpandableBufferOutputStream; -using session_ident_type = uint_fast16_t; -using file_ident_type = uint_fast64_t; -using version_type = uint_fast64_t; -using timestamp_type = uint_fast64_t; -using request_ident_type = uint_fast64_t; - - - - -class ClientProtocol { -public: - util::Logger& logger; - - enum class Error { - unknown_message = 101, // Unknown type of input message - bad_syntax = 102, // Bad syntax in input message head - limits_exceeded = 103, // Limits exceeded in input message - bad_changeset_header_syntax = 108, // Bad syntax in changeset header (DOWNLOAD) - bad_changeset_size = 109, // Bad changeset size in changeset header (DOWNLOAD) - bad_server_version = 111, // Bad server version in changeset header (DOWNLOAD) - bad_error_code = 114, ///< Bad error code (ERROR) - bad_decompression = 115, // Error in decompression (DOWNLOAD) - }; - - ClientProtocol(util::Logger& logger); - - - /// Messages sent by the client. - - void make_client_message(OutputBuffer& out, const std::string& client_info); - - void make_bind_message(OutputBuffer& out, session_ident_type session_ident, - const std::string& server_path, - const std::string& signed_user_token, - bool need_file_ident_pair); - - void make_refresh_message(OutputBuffer& out, session_ident_type session_ident, - const std::string& signed_user_token); - - void make_ident_message(OutputBuffer& out, session_ident_type session_ident, - file_ident_type server_file_ident, - file_ident_type client_file_ident, - int_fast64_t client_file_ident_secret, - SyncProgress progress); - - void make_upload_message(OutputBuffer& out, session_ident_type session_ident, - version_type client_version, version_type server_version, - size_t changeset_size, timestamp_type timestamp, - const std::unique_ptr& body_buffer); - - void make_unbind_message(OutputBuffer& out, session_ident_type session_ident); - - void make_mark_message(OutputBuffer& out, session_ident_type session_ident, - request_ident_type request_ident); - - - - // Messages received by the client. - - // parse_message_received takes a (WebSocket) message and parses it. - // The result of the parsing is handled by an object of type Connection. - // Typically, Connection would be the Connection class from client.cpp - template - void parse_message_received(Connection& connection, const char* data, size_t size) - { - util::MemoryInputStream in; - in.set_buffer(data, data + size); - in.unsetf(std::ios_base::skipws); - size_t header_size = 0; - std::string message_type; - in >> message_type; - logger.debug("message_type = %1", message_type); - - if (message_type == "download") { - session_ident_type session_ident; - SyncProgress progress; - int is_body_compressed; - size_t uncompressed_body_size, compressed_body_size; - char sp_1, sp_2, sp_3, sp_4, sp_5, sp_6, sp_7, sp_8, sp_9, sp_10, newline; - - in >> sp_1 >> session_ident >> sp_2 >> progress.scan_server_version >> sp_3 >> - progress.scan_client_version >> sp_4 >> progress.latest_server_version >> - sp_5 >> progress.latest_server_session_ident >> sp_6 >> - progress.latest_client_version >> sp_7 >> progress.downloadable_bytes >> - sp_8 >> is_body_compressed >> sp_9 >> uncompressed_body_size >> sp_10 >> - compressed_body_size >> newline; // Throws - - bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' && - sp_3 == ' ' && sp_4 == ' ' && sp_5 == ' ' && sp_6 == ' ' && - sp_7 == ' ' && sp_8 == ' ' && sp_9 == ' ' && sp_10 == ' ' && - newline == '\n'; - if (!good_syntax) - goto bad_syntax; - header_size = size_t(in.tellg()); - if (uncompressed_body_size > s_max_body_size) - goto limits_exceeded; - - size_t body_size = is_body_compressed ? compressed_body_size : uncompressed_body_size; - if (header_size + body_size != size) - goto bad_syntax; - - BinaryData body(data + header_size, body_size); - BinaryData uncompressed_body; - - std::unique_ptr uncompressed_body_buffer; - // if is_body_compressed == true, we must decompress the received body. - if (is_body_compressed) { - uncompressed_body_buffer.reset(new char[uncompressed_body_size]); - std::error_code ec = util::compression::decompress(body.data(), compressed_body_size, - uncompressed_body_buffer.get(), - uncompressed_body_size); - - if (ec) { - logger.error("compression::inflate: %1", ec.message()); - connection.handle_protocol_error(Error::bad_decompression); - return; - } - - uncompressed_body = BinaryData(uncompressed_body_buffer.get(), uncompressed_body_size); - } - else { - uncompressed_body = body; - } - - logger.debug("Download message compression: is_body_compressed = %1, " - "compressed_body_size=%2, uncompressed_body_size=%3", - is_body_compressed, compressed_body_size, uncompressed_body_size); - - util::MemoryInputStream in; - in.unsetf(std::ios_base::skipws); - in.set_buffer(uncompressed_body.data(), uncompressed_body.data() + uncompressed_body_size); - - std::vector received_changesets; - - // Loop through the body and find the changesets. - size_t position = 0; - while (position < uncompressed_body_size) { - version_type server_version; - version_type client_version; - timestamp_type origin_timestamp; - file_ident_type origin_client_file_ident; - size_t changeset_size; - char sp_1, sp_2, sp_3, sp_4, sp_5; - - in >> server_version >> sp_1 >> client_version >> sp_2 >> origin_timestamp >> - sp_3 >> origin_client_file_ident >> sp_4 >> changeset_size >> sp_5; - - bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' && - sp_3 == ' ' && sp_4 == ' ' && sp_5 == ' '; - - if (!good_syntax) { - logger.error("Bad changeset header syntax"); - connection.handle_protocol_error(Error::bad_changeset_header_syntax); - return; - } - - // Update position to the end of the change set - position = size_t(in.tellg()) + changeset_size; - - if (position > uncompressed_body_size) { - logger.error("Bad changeset size"); - connection.handle_protocol_error(Error::bad_changeset_size); - return; - } - - if (server_version == 0) { - // The received changeset can never have version 0. - logger.error("Bad server version"); - connection.handle_protocol_error(Error::bad_server_version); - return; - } - - BinaryData changeset_data(uncompressed_body.data() + size_t(in.tellg()), changeset_size); - in.seekg(position); - - if (logger.would_log(util::Logger::Level::trace)) { - logger.trace("Received: DOWNLOAD CHANGESET(server_version=%1, client_version=%2, " - "origin_timestamp=%3, origin_client_file_ident=%4, changeset_size=%5)", - server_version, client_version, origin_timestamp, - origin_client_file_ident, changeset_size); // Throws - logger.trace("Changeset: %1", util::hex_dump(changeset_data.data(), changeset_size)); // Throws - } - - Transformer::RemoteChangeset changeset_2(server_version, client_version, - changeset_data, origin_timestamp, - origin_client_file_ident); - received_changesets.push_back(changeset_2); // Throws - } - - connection.receive_download_message(session_ident, progress, received_changesets); // Throws - return; - } - if (message_type == "unbound") { - session_ident_type session_ident; - char sp_1, newline; - in >> sp_1 >> session_ident >> newline; // Throws - bool good_syntax = in && size_t(in.tellg()) == size && sp_1 == ' ' && - newline == '\n'; - if (!good_syntax) - goto bad_syntax; - header_size = size_t(in.tellg()); - - connection.receive_unbound_message(session_ident); // Throws - return; - } - if (message_type == "error") { - int error_code; - size_t message_size; - bool try_again; - session_ident_type session_ident; - char sp_1, sp_2, sp_3, sp_4, newline; - in >> sp_1 >> error_code >> sp_2 >> message_size >> sp_3 >> try_again >> sp_4 >> - session_ident >> newline; // Throws - bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' && sp_3 == ' ' && - sp_4 == ' ' && newline == '\n'; - if (!good_syntax) - goto bad_syntax; - header_size = size_t(in.tellg()); - if (header_size + message_size != size) - goto bad_syntax; - - bool unknown_error = !get_protocol_error_message(error_code); - if (unknown_error) { - logger.error("Bad error code"); // Throws - connection.handle_protocol_error(Error::bad_error_code); - return; - } - - std::string message{data + header_size, message_size}; // Throws (copy) - - connection.receive_error_message(error_code, message_size, try_again, session_ident, message); // Throws - return; - } - if (message_type == "mark") { - session_ident_type session_ident; - request_ident_type request_ident; - char sp_1, sp_2, newline; - in >> sp_1 >> session_ident >> sp_2 >> request_ident >> newline; // Throws - bool good_syntax = in && size_t(in.tellg()) == size && sp_1 == ' ' && - sp_2 == ' ' && newline == '\n'; - if (!good_syntax) - goto bad_syntax; - header_size = size_t(in.tellg()); - - connection.receive_mark_message(session_ident, request_ident); // Throws - return; - } - if (message_type == "alloc") { - session_ident_type session_ident; - file_ident_type server_file_ident, client_file_ident; - int_fast64_t client_file_ident_secret; - char sp_1, sp_2, sp_3, sp_4, newline; - in >> sp_1 >> session_ident >> sp_2 >> server_file_ident >> sp_3 >> - client_file_ident >> sp_4 >> client_file_ident_secret >> newline; // Throws - bool good_syntax = in && size_t(in.tellg()) == size && sp_1 == ' ' && - sp_2 == ' ' && sp_3 == ' ' && sp_4 == ' ' && newline == '\n'; - if (!good_syntax) - goto bad_syntax; - header_size = size_t(in.tellg()); - - connection.receive_alloc_message(session_ident,server_file_ident, client_file_ident, - client_file_ident_secret); // Throws - return; - } - - logger.error("Unknown input message type '%1'", - StringData(data, size)); - connection.handle_protocol_error(Error::unknown_message); - return; - bad_syntax: - logger.error("Bad syntax in input message '%1'", - StringData(data, size)); - connection.handle_protocol_error(Error::bad_syntax); - return; - limits_exceeded: - logger.error("Limits exceeded in input message '%1'", - StringData(data, header_size)); - connection.handle_protocol_error(Error::limits_exceeded); - return; - } - -private: - static constexpr size_t s_max_body_size = std::numeric_limits::max(); -}; - - -class ServerProtocol { -public: - util::Logger& logger; - - enum class Error { - unknown_message = 101, // Unknown type of input message - bad_syntax = 102, // Bad syntax in input message head - limits_exceeded = 103, // Limits exceeded in input message - }; - - ServerProtocol(util::Logger& logger); - - // Messages sent by the server to the client - - void make_alloc_message(OutputBuffer& out, session_ident_type session_ident, - file_ident_type server_file_ident, file_ident_type client_file_ident, - int_fast64_t client_file_ident_secret); - - void make_unbound_message(OutputBuffer& out, session_ident_type session_ident); - - - class ChangesetInfo { - public: - version_type server_version; - version_type client_version; - HistoryEntry entry; - - ChangesetInfo(version_type server_version, version_type client_version, HistoryEntry entry): - server_version(server_version), - client_version(client_version), - entry(entry) - {} - }; - - void make_download_message(int protocol_version, OutputBuffer& out, session_ident_type session_ident, - version_type scan_server_version, - version_type scan_client_version, - version_type latest_server_version, - int_fast64_t latest_server_session_ident, - version_type latest_client_version, - uint_fast64_t downloadable_bytes, - const std::vector& changeset_infos); - - void make_error_message(OutputBuffer& out, ProtocolError error_code, - const char* message, size_t message_size, - bool try_again, session_ident_type session_ident); - - void make_mark_message(OutputBuffer& out, session_ident_type session_ident, - request_ident_type request_ident); - - // Messages received by the server. - - // parse_message_received takes a (WebSocket) message and parses it. - // The result of the parsing is handled by an object of type Connection. - // Typically, Connection would be the Connection class from server.cpp - template - void parse_message_received(Connection& connection, const char* data, size_t size) - { - util::MemoryInputStream in; - in.set_buffer(data, data + size); - in.unsetf(std::ios_base::skipws); - size_t header_size = 0; - std::string message_type; - in >> message_type; - - if (message_type == "upload") { - session_ident_type session_ident; - version_type client_version, server_version; - size_t changeset_size; - timestamp_type timestamp; - char sp_1, sp_2, sp_3, sp_4, sp_5, newline; - in >> sp_1 >> session_ident >> sp_2 >> client_version >> sp_3 >> - server_version >> sp_4 >> changeset_size >> sp_5 >> timestamp >> - newline; - bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' && sp_3 == ' ' && - sp_4 == ' ' && sp_5 == ' ' && newline == '\n'; - if (!good_syntax) - goto bad_syntax; - header_size = size_t(in.tellg()); - if (changeset_size > s_max_changeset_size) - goto limits_exceeded; - if (header_size + changeset_size != size) - goto bad_syntax; - - BinaryData changeset(data + header_size, changeset_size); - - connection.receive_upload_message(session_ident, client_version, - server_version, changeset, - timestamp); // Throws - return; - } - if (message_type == "mark") { - session_ident_type session_ident; - request_ident_type request_ident; - char sp_1, sp_2, newline; - in >> sp_1 >> session_ident >> sp_2 >> request_ident >> newline; - bool good_syntax = in && size_t(in.tellg()) == size && - sp_1 == ' ' && sp_2 == ' ' && newline == '\n'; - if (!good_syntax) - goto bad_syntax; - header_size = size; - - connection.receive_mark_message(session_ident, request_ident); // Throws - return; - } - if (message_type == "bind") { - session_ident_type session_ident; - size_t path_size; - size_t signed_user_token_size; - bool need_file_ident_pair; - char sp_1, sp_2, sp_3, sp_4, newline; - in >> sp_1 >> session_ident >> sp_2 >> path_size >> sp_3 >> - signed_user_token_size >> sp_4 >> need_file_ident_pair >> - newline; - bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' && - sp_3 == ' ' && sp_4 == ' ' && newline == '\n'; - if (!good_syntax) - goto bad_syntax; - header_size = size_t(in.tellg()); - if (path_size == 0) - goto bad_syntax; - if (path_size > s_max_path_size) - goto limits_exceeded; - if (signed_user_token_size > s_max_signed_user_token_size) - goto limits_exceeded; - if (header_size + path_size + signed_user_token_size != size) - goto bad_syntax; - - std::string path {data + header_size, path_size}; // Throws - std::string signed_user_token {data + header_size + path_size, - signed_user_token_size}; // Throws - - connection.receive_bind_message(session_ident, std::move(path), - std::move(signed_user_token), - need_file_ident_pair); // Throws - return; - } - if (message_type == "refresh") { - session_ident_type session_ident; - size_t signed_user_token_size; - char sp_1, sp_2, newline; - in >> sp_1 >> session_ident >> sp_2 >> signed_user_token_size >> - newline; - bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' && newline == '\n'; - if (!good_syntax) - goto bad_syntax; - header_size = size_t(in.tellg()); - if (signed_user_token_size > s_max_signed_user_token_size) - goto limits_exceeded; - if (header_size + signed_user_token_size != size) - goto bad_syntax; - - std::string signed_user_token {data + header_size, signed_user_token_size}; - - connection.receive_refresh_message(session_ident, std::move(signed_user_token)); // Throws - return; - } - if (message_type == "ident") { - session_ident_type session_ident; - file_ident_type server_file_ident, client_file_ident; - int_fast64_t client_file_ident_secret; - version_type scan_server_version, scan_client_version, latest_server_version; - int_fast64_t latest_server_session_ident; - char sp_1, sp_2, sp_3, sp_4, sp_5, sp_6, sp_7, sp_8, newline; - in >> sp_1 >> session_ident >> sp_2 >> server_file_ident >> sp_3 >> - client_file_ident >> sp_4 >> client_file_ident_secret >> sp_5 >> - scan_server_version >> sp_6 >> scan_client_version >> sp_7 >> - latest_server_version >> sp_8 >> latest_server_session_ident >> - newline; - bool good_syntax = in && size_t(in.tellg()) == size && sp_1 == ' ' && - sp_2 == ' ' && sp_3 == ' ' && sp_4 == ' ' && sp_5 == ' ' && - sp_6 == ' ' && sp_7 == ' ' && sp_8 == ' ' && newline == '\n'; - if (!good_syntax) - goto bad_syntax; - header_size = size; - - connection.receive_ident_message(session_ident, server_file_ident, client_file_ident, - client_file_ident_secret, scan_server_version, scan_client_version, - latest_server_version, latest_server_session_ident); // Throws - return; - } - if (message_type == "unbind") { - session_ident_type session_ident; - char sp_1, newline; - in >> sp_1 >> session_ident >> newline; - bool good_syntax = in && size_t(in.tellg()) == size && - sp_1 == ' ' && newline == '\n'; - if (!good_syntax) - goto bad_syntax; - header_size = size; - - connection.receive_unbind_message(session_ident); // Throws - return; - } - if (message_type == "client") { - int_fast64_t protocol_version; - char sp_1, sp_2, newline; - size_t client_info_size; - in >> sp_1 >> protocol_version >> sp_2 >> client_info_size >> newline; - bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' && newline == '\n'; - if (!good_syntax) - goto bad_syntax; - header_size = size_t(in.tellg()); - bool limits_exceeded = (client_info_size > s_max_client_info_size); - if (limits_exceeded) - goto limits_exceeded; - if (header_size + client_info_size != size) - goto bad_syntax; - - std::string client_info {data + header_size, client_info_size}; // Throws - - connection.receive_client_message(protocol_version, std::move(client_info)); // Throws - return; - } - - // unknown message - if (size < 256) - logger.error("Unknown input message type '%1'", StringData(data, size)); // Throws - else - logger.error("Unknown input message type '%1'.......", StringData(data, 256)); // Throws - - connection.handle_protocol_error(Error::unknown_message); - return; - - bad_syntax: - logger.error("Bad syntax in input message '%1'", - StringData(data, size)); - connection.handle_protocol_error(Error::bad_syntax); // Throws - return; - limits_exceeded: - logger.error("Limits exceeded in input message '%1'", - StringData(data, header_size)); - connection.handle_protocol_error(Error::limits_exceeded); // Throws - return; - } - -private: - static constexpr size_t s_max_head_size = 256; - static constexpr size_t s_max_signed_user_token_size = 2048; - static constexpr size_t s_max_client_info_size = 1024; - static constexpr size_t s_max_path_size = 1024; - static constexpr size_t s_max_changeset_size = std::numeric_limits::max(); - - util::compression::CompressMemoryArena m_compress_memory_arena; - - // Permanent buffer to use for internal purposes such as compression. - std::vector m_buffer; - - // Outputbuffer to use for internal purposes such as creating the - // download body. - OutputBuffer m_output_buffer; - - void insert_single_changeset_download_message(OutputBuffer& out, const ChangesetInfo& changeset_info); -}; -} // namespace protocol } // namespace sync } // namespace realm diff --git a/Pods/Realm/include/core/realm/sync/server.hpp b/Pods/Realm/include/core/realm/sync/server.hpp deleted file mode 100644 index 2490b5c..0000000 --- a/Pods/Realm/include/core/realm/sync/server.hpp +++ /dev/null @@ -1,181 +0,0 @@ -/************************************************************************* - * - * REALM CONFIDENTIAL - * __________________ - * - * [2011] - [2012] Realm Inc - * All Rights Reserved. - * - * NOTICE: All information contained herein is, and remains - * the property of Realm Incorporated and its suppliers, - * if any. The intellectual and technical concepts contained - * herein are proprietary to Realm Incorporated - * and its suppliers and may be covered by U.S. and Foreign Patents, - * patents in process, and are protected by trade secret or copyright law. - * Dissemination of this information or reproduction of this material - * is strictly forbidden unless prior written permission is obtained - * from Realm Incorporated. - * - **************************************************************************/ -#ifndef REALM_SYNC_SERVER_HPP -#define REALM_SYNC_SERVER_HPP - -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace realm { -namespace sync { - -class Server { -public: - class Clock; - - struct Config { - Config() {} - - /// The maximum number of Realm files that will be kept open - /// concurrently by this server. The server keeps a cache of open Realm - /// files for efficiency reasons. - long max_open_files = 256; - - /// An optional time provider to be used by the server. - /// If no time provider is specified, the server will use the - /// system clock. - Clock* clock = nullptr; - - /// An optional logger to be used by the server. If no logger is - /// specified, the server will use an instance of util::StderrLogger - /// with the log level threshold set to util::Logger::Level::info. The - /// server does not require a thread-safe logger, and it guarantees that - /// all logging happens on behalf of start() and run() (which are not - /// allowed to execute concurrently). - util::Logger* logger = nullptr; - - /// An optional sink for recording metrics about the internal operation - /// of the server. Below is a list of counters and gauges that are - /// updated by the server. The server may or may not update additional - /// counters and gauges. - /// - /// Statistics counters Incremented when - /// ------------------------------------------------------------------------ - /// server.started The server was started - /// connection.started A new client connection was established - /// connection.terminated A client connection was terminated - /// session.started A new session was started - /// session.terminated A session was terminated - /// connection.read.failed A connection was closed due to read error - /// connection.write.failed A connection was closed due to write error - /// protocol.upload.received An UPLOAD message was received - /// protocol.download.sent A DOWNLOAD message was sent - /// protocol.connection.errored Connection level protocol error occurred - /// protocol.session.errored Session level protocol error occurred - /// - /// Statistics gauges Continuously updated to reflect - /// -------------------------------------------------------------------------- - /// connection.opened The current total number of connections - /// session.opened The current total number of sessions - /// - Metrics* metrics = nullptr; - - /// FIXME: This seems to be related to the dashboard feature, but it - /// would be nice with some additional explanation (Sebastian). - const char* stats_db = nullptr; - - /// The address at which the listening socket is bound. - /// The address can be a name or on numerical form. - /// Use "localhost" to listen on the loopback interface. - std::string listen_address; - - /// The port at which the listening socket is bound. - /// The port can be a name or on numerical form. - /// Use the empty string to have the system assign a dynamic - /// listening port. - std::string listen_port; - - bool reuse_address = true; - - /// The listening socket accepts TLS/SSL connections if `ssl` is - /// true, and non-secure tcp connections otherwise. - bool ssl = false; - - /// The path of the certificate that will be sent to clients during - /// the SSL/TLS handshake. - /// - /// From the point of view of OpenSSL, this file will be passed to - /// `SSL_CTX_use_certificate_chain_file()`. - /// - /// This option is ignore if `ssl` is false. - std::string ssl_certificate_path; - - /// The path of the private key corresponding to the certificate. - /// - /// From the point of view of OpenSSL, this file will be passed to - /// `SSL_CTX_use_PrivateKey_file()`. - /// - /// This option is ignore if `ssl` is false. - std::string ssl_certificate_key_path; - }; - - Server(const std::string& root_dir, util::Optional public_key, Config = {}); - Server(Server&&) noexcept; - ~Server() noexcept; - - /// start() binds a listening socket to the address and port specified in - /// Config and starts accepting connections. - /// The resolved endpoint (including the dynamically assigned port, if requested) - /// can be obtained by calling listen_endpoint(). - /// This can be done immediately after start() returns. - void start(); - - /// A helper function, for backwards compatibility, that starts a listening - /// socket without SSL at the specified address and port. - void start(const std::string& listen_address, - const std::string& listen_port, - bool reuse_address = true); - - /// Return the resolved and bound endpoint of the listening socket. - util::network::Endpoint listen_endpoint() const; - - /// Run the internal event-loop of the server. At most one thread may - /// execute run() at any given time. It is an error if run() is called - /// before start() has been successfully executed. The call to run() will - /// not return until somebody calls stop(). - void run(); - - /// Stop any thread that is currently executing run(). This function may be - /// called by any thread. - void stop() noexcept; - - /// Must not be called while run() is executing. - uint_fast64_t errors_seen() const noexcept; - - /// Initialise the directory structure as required for correct operation of - /// the server. This is a static function, as it should be run on the \a - /// root_path prior to instantiating the \c Server object. - static void init_directory_structure(const std::string& root_path, util::Logger& logger); - - -private: - class Implementation; - std::unique_ptr m_impl; -}; - - -class Server::Clock { -public: - virtual int_fast64_t now() = 0; - - virtual ~Clock() {} -}; - -} // namespace sync -} // namespace realm - -#endif // REALM_SYNC_SERVER_HPP diff --git a/Pods/Realm/include/core/realm/sync/server_configuration.hpp b/Pods/Realm/include/core/realm/sync/server_configuration.hpp deleted file mode 100644 index 5014495..0000000 --- a/Pods/Realm/include/core/realm/sync/server_configuration.hpp +++ /dev/null @@ -1,57 +0,0 @@ -/************************************************************************* - * - * REALM CONFIDENTIAL - * __________________ - * - * [2011] - [2015] Realm Inc - * All Rights Reserved. - * - * NOTICE: All information contained herein is, and remains - * the property of Realm Incorporated and its suppliers, - * if any. The intellectual and technical concepts contained - * herein are proprietary to Realm Incorporated - * and its suppliers and may be covered by U.S. and Foreign Patents, - * patents in process, and are protected by trade secret or copyright law. - * Dissemination of this information or reproduction of this material - * is strictly forbidden unless prior written permission is obtained - * from Realm Incorporated. - * - **************************************************************************/ -#ifndef REALM_SYNC_SERVER_CONFIGURATION_HPP -#define REALM_SYNC_SERVER_CONFIGURATION_HPP - -// Realm headers -#include -#include - -namespace realm { -namespace config { - -struct Configuration { - std::string listen_address = "127.0.0.1"; - std::string listen_port = ""; // Empty means choose default based on `ssl`. - realm::util::Optional root_dir; - std::string user_data_dir; - std::string internal_data_dir; - realm::util::Optional public_key_path; - realm::util::Optional config_file_path; - bool reuse_address = true; - bool disable_sync = false; - realm::util::Logger::Level log_level = realm::util::Logger::Level::info; - realm::util::Optional log_path; - std::string stats_db_path; - long max_open_files = 256; - bool ssl = false; - std::string ssl_certificate_path; - std::string ssl_certificate_key_path; - std::string dashboard_stats_endpoint = "localhost:28125"; -}; - -void show_help(const std::string& program_name); -Configuration build_configuration(int argc, char* argv[]); -Configuration load_configuration(std::string configuration_file_path); - -} // namespace config -} // namespace realm - -#endif // REALM_SYNC_SERVER_CONFIGURATION_HPP diff --git a/Pods/Realm/include/core/realm/sync/transform.hpp b/Pods/Realm/include/core/realm/sync/transform.hpp index 5d9b115..8809c38 100644 --- a/Pods/Realm/include/core/realm/sync/transform.hpp +++ b/Pods/Realm/include/core/realm/sync/transform.hpp @@ -24,249 +24,135 @@ #include #include -#include +#include #include +#include +#include namespace realm { namespace sync { +struct Changeset; + +/// Represents an entry in the history of changes in a sync-enabled Realm +/// file. Server and client use different history formats, but this class is +/// used both on the server and the client side. Each history entry corresponds +/// to a version of the Realm state. For server and client-side histories, these +/// versions are referred to as *server versions* and *client versions* +/// respectively. These versions may, or may not correspond to Realm snapshot +/// numbers (on the server-side they currently do not). +/// +/// FIXME: Move this class into a separate header +/// (``). class HistoryEntry { public: - using timestamp_type = uint_fast64_t; - using file_ident_type = uint_fast64_t; - using version_type = _impl::History::version_type; - /// The time of origination of the changes referenced by this history entry, /// meassured as the number of milliseconds since 2015-01-01T00:00:00Z, not /// including leap seconds. For changes of local origin, this is the local /// time at the point when the local transaction was committed. For changes - /// of remote origin, it is the remote time of origin at the client - /// identified by `origin_client_file_ident`. - /// - /// All clients that will be, or are already participating in - /// synchronization must guarantee that their local history is causally - /// consistent. The convergence guarantee offered by the merge system relies - /// strongly on this. - /// - /// FIXME: In its current form, the merge algorithm seems to achieve - /// convergence even without causal consistency. Figure out whether we still - /// want to require it, and if so, why. - /// - /// **Definition:** The local history is *causally consistent* if, and only - /// if every entry, referring to changes of local origin, has an effective - /// timestamp, which is greater than, or equal to the effective timestamp of - /// all preceding entries in the local history. - /// - /// **Definition:** The *effective timestamp* of a history entry is the pair - /// `(origin_timestamp, origin_client_file_ident)` endowed with the standard - /// lexicographic order. Note that this implies that it is impossible for - /// two entries to have equal effective timestamps if they originate from - /// different clients. + /// of remote origin, it is the remote time of origin at the peer (client or + /// server) identified by `origin_file_ident`. timestamp_type origin_timestamp; - /// For changes of local origin, `origin_client_file_ident` is always - /// zero. For changes of remote origin, this history entry was produced by - /// the integration of a changeset received from a remote peer P. In some - /// cases, that changeset may itself have been produced by the integration - /// on P of a changeset received from another remote peer. In any case, as - /// long as these changes are of remote origin, `origin_client_file_ident` - /// identifies the peer on which they originated, which may or may not be P. - /// - /// More concretely, on the server-side, the remote peer is a client, and - /// and the changes always originate from that client, so - /// `origin_client_file_ident` always refer to that client. Conversely, on - /// the client-side, the remote peer is the server, and the server received - /// the original changeset from a client, so `origin_client_file_ident` - /// refers to that client. + /// The identifier of the file in the context of which the initial + /// untransformed changeset originated, or zero if the changeset originated + /// on the local peer (client or server). /// - /// Note that *peer* is used colloquially here to refer to a particular - /// synchronization participant. In reality, a synchronization participant - /// is either a server-side file, or a particular client-side file - /// associated with that server-side file. To make things even more - /// confusing, a single client application may contain multiple client-side - /// files associated with the same server-side file. In the same vein, - /// *client* should be understood as client-side file, and *remote peer* as - /// any other file from the set of associated files, even other such files - /// contained within the same client application, if there are any. - file_ident_type origin_client_file_ident; - - /// For changes of local origin, `remote_version` is the version produced on - /// the remote peer by the last changeset integrated locally prior to the - /// production of the changeset referenced by this history entry, or zero if - /// no remote changeset was integrated yet. This only makes sense when there - /// is a unique remote peer, and since that is not the case on the server, - /// the server cannot be the originator of any changes. + /// For example, when a changeset "travels" from a file with identifier 2 on + /// client A, through a file with identifier 1 on the server, to a file with + /// identifier 3 on client B, then `origin_file_ident` will be 0 on client + /// A, 2 on the server, and 2 on client B. On the other hand, if the server + /// was the originator of the changeset, then `origin_file_ident` would be + /// zero on the server, and 1 on both clients. + file_ident_type origin_file_ident; + + /// For changes of local origin on the client side, this is the last server + /// version integrated locally prior to this history entry. In other words, + /// it is a copy of `remote_version` of the last preceding history entry + /// that carries changes of remote origin, or zero if there is no such + /// preceding history entry. /// - /// For changes of remote origin, this history entry was produced by the - /// integration of a changeset directly received from a remote peer P, and - /// `remote_version` is then the version produced on P by that - /// changeset. Note that such changes may have originated from a different - /// peer (not P), but `remote_version` will still be the version produced on - /// P. + /// For changes of local origin on the server-side, this is always zero. /// - /// More concretely, on the server-side, the remote peer is a client, and - /// the changes always originate from that client, and `remote_version` is - /// the `` specified in an UPLOAD message of the - /// client-server communication protocol. Conversely, for changes of remote - /// origin on the client-side, the remote peer is the server, and - /// `remote_version` is the specified in a received - /// DOWNLOAD message. + /// For changes of remote origin, this is the version produced within the + /// remote-side Realm file by the change that gave rise to this history + /// entry. The remote-side Realm file is not always the same Realm file from + /// which the change originated. On the client side, the remote side is + /// always the server side, and `remote_version` is always a server version + /// (since clients do not speak directly to each other). On the server side, + /// the remote side is always a client side, and `remote_version` is always + /// a client version. version_type remote_version; /// Referenced memory is not owned by this class. - BinaryData changeset; + ChunkedBinaryData changeset; }; + /// The interface between the sync history and the operational transformer -/// (Transformer). +/// (Transformer) for the purpose of transforming changesets received from a +/// particular *remote peer*. class TransformHistory { public: - using timestamp_type = HistoryEntry::timestamp_type; - using file_ident_type = HistoryEntry::file_ident_type; - using version_type = HistoryEntry::version_type; - - /// Get the first history entry whose changeset produced a version that - /// succeeds `begin_version` and, and does not succeed `end_version`, and - /// whose changeset was not produced by integration of a changeset received - /// from the specified remote peer. + /// Get the first history entry where the produced version is greater than + /// `begin_version` and less than or equal to `end_version`, and whose + /// changeset is neither empty, nor produced by integration of a changeset + /// received from the remote peer associated with this history. /// - /// The ownership of the memory referenced by `entry.changeset` is **not** - /// passed to the caller upon return. The callee retains ownership. + /// If \a buffer is non-null, memory will be allocated and transferred to + /// \a buffer. The changeset will be copied into the newly allocated memory. + /// + /// If \a buffer is null, the changeset is not copied out of the Realm, + /// and entry.changeset.data() does not point to the changeset. + /// The changeset in the Realm could be chunked, hence it is not possible + /// to point to it with BinaryData. entry.changeset.size() always gives the + /// size of the changeset. /// /// \param begin_version, end_version The range of versions to consider. If - /// `begin_version` is equal to `end_version`, this is the empty range. If + /// `begin_version` is equal to `end_version`, it is the empty range. If /// `begin_version` is zero, it means that everything preceeding /// `end_version` is to be considered, which is again the empty range if - /// `end_version` is also zero. Zero is is special value in that no - /// changeset produces that version. It is an error if `end_version` - /// preceeds `begin_version`, or if `end_version` is zero and - /// `begin_version` is not. - /// - /// \param not_from_remote_client_file_ident Skip entries whose changeset is - /// produced by integration of changesets received from this remote - /// peer. Zero if the remote peer is the server, otherwise the peer - /// identifier of a client. - /// - /// \param only_nonempty Skip entries with empty changesets. + /// `end_version` is also zero. Zero is a special value in that no changeset + /// produces that version. It is an error if `end_version` precedes + /// `begin_version`, or if `end_version` is zero and `begin_version` is not. /// /// \return The version produced by the changeset of the located history - /// entry, or zero if no history entry exists matching the specified - /// criteria. + /// entry, or zero if no history entry exists matching the specified and + /// implied criteria. virtual version_type find_history_entry(version_type begin_version, version_type end_version, - file_ident_type not_from_remote_client_file_ident, - bool only_nonempty, HistoryEntry& entry) const noexcept = 0; - /// Copy a contiguous sequence of bytes from the specified reciprocally - /// transformed changeset into the specified buffer. The targeted history - /// entry is the one whose untransformed changeset produced the specified - /// version. Copying starts at the specified offset within the transform, - /// and will continue until the end of the transform or the end of the - /// buffer, whichever comes first. The first copied byte is always placed in - /// `buffer[0]`. The number of copied bytes is returned. - /// - /// \param remote_client_file_ident Zero if the remote peer is the server, - /// otherwise the peer identifier of a client. - virtual size_t read_reciprocal_transform(version_type version, - file_ident_type remote_client_file_ident, - size_t offset, char* buffer, size_t size) const = 0; - - /// Replace a contiguous chunk of bytes within the specified reciprocally - /// transformed changeset. The targeted history entry is the one whose - /// untransformed changeset produced the specified version. If the new chunk - /// has a different size than the on it replaces, subsequent bytes (those - /// beyond the end of the replaced chunk) are shifted to lower or higher - /// offsets accordingly. If `replaced_size` is equal to `size_t(-1)`, the - /// replaced chunk extends from `offset` to the end of the transform. Let - /// `replaced_size_2` be the actual size of the replaced chunk, then the - /// total number of bytes in the transform will increase by `size - - /// replaced_size_2`. It is an error if `replaced_size` is not `size_t(-1)` - /// and `offset + replaced_size` is greater than the size of the transform. - /// - /// \param remote_client_file_ident See read_reciprocal_transform(). - /// - /// \param offset The index of the first replaced byte relative to the - /// beginning of the transform. - /// - /// \param replaced_size The number of bytes in the replaced chunk. - /// - /// \param data The buffer holding the replacing chunk. + /// Get the specified reciprocal changeset. The targeted history entry is + /// the one whose untransformed changeset produced the specified version. + virtual ChunkedBinaryData get_reciprocal_transform(version_type version) const = 0; + + /// Replace the specified reciprocally transformed changeset. The targeted + /// history entry is the one whose untransformed changeset produced the + /// specified version. /// - /// \param size The number of bytes in the replacing chunk, which is also - /// the number of bytes that will be read from the specified buffer. - virtual void write_reciprocal_transform(version_type version, - file_ident_type remote_client_file_ident, - size_t offset, size_t replaced_size, - const char* data, size_t size) = 0; + /// \param encoded_changeset The new reciprocally transformed changeset. + virtual void set_reciprocal_transform(version_type version, BinaryData encoded_changeset) = 0; virtual ~TransformHistory() noexcept {} - -protected: - static bool register_local_time(timestamp_type local_timestamp, - timestamp_type& timestamp_threshold) noexcept; - - static bool register_remote_time(timestamp_type remote_timestamp, - timestamp_type& timestamp_threshold) noexcept; }; -class TransformError; // Exception +class TransformError; // Exception class Transformer { public: - using timestamp_type = HistoryEntry::timestamp_type; - using file_ident_type = HistoryEntry::file_ident_type; - using version_type = HistoryEntry::version_type; - - struct RemoteChangeset { - /// The version produced on the remote peer by this changeset. - /// - /// On the server, the remote peer is the client from which the - /// changeset originated, and `remote_version` is the client version - /// produced by the changeset on that client. - /// - /// On a client, the remote peer is the server, and `remote_version` is - /// the server version produced by this changeset on the server. Since - /// the server is never the originator of changes, this changeset must - /// in turn have been produced on the server by integration of a - /// changeset uploaded by some other client. - version_type remote_version; - - /// The last local version that has been integrated into - /// `remote_version`. - /// - /// A local version, L, has been integrated into a remote version, R, - /// when, and only when L is the latest local version such that all - /// preceeding changesets in the local history have been integrated by - /// the remote peer prior to R. - /// - /// On the server, this is the last server version integrated into the - /// client version `remote_version`. On a client, it is the last client - /// version integrated into the server version `remote_version`. - version_type last_integrated_local_version; - - /// The changeset itself. - BinaryData data; - - /// Same meaning as `HistoryEntry::origin_timestamp`. - timestamp_type origin_timestamp; - - /// Same meaning as `HistoryEntry::origin_client_file_ident`. - file_ident_type origin_client_file_ident; - - RemoteChangeset(version_type rv, version_type lv, BinaryData d, timestamp_type ot, - file_ident_type fi); - }; - - /// Produce an operationally transformed version of the specified changeset, - /// which is assumed to be of remote origin, and received from remote peer - /// P. Note that P is not necessarily the peer from which the changes - /// originated. + class RemoteChangeset; + class Reporter; + + /// Produce operationally transformed versions of the specified changesets, + /// which are assumed to be received from a particular remote peer, P, + /// represented by the specified transform history. Note that P is not + /// necessarily the peer on which the changes originated. /// /// Operational transformation is carried out between the specified - /// changeset and all causally unrelated changesets in the local history. A + /// changesets and all causally unrelated changesets in the local history. A /// changeset in the local history is causally unrelated if, and only if it /// occurs after the local changeset that produced /// `remote_changeset.last_integrated_local_version` and is not a produced @@ -281,9 +167,7 @@ class Transformer { /// TransformHistory::get_history_entry()), instead the reciprocally /// transformed changesets are stored separately, and individually for each /// remote peer, such that they can participate in transformation of the - /// next incoming changeset from P. Note that the peer identifier of P can - /// be derived from `origin_client_file_ident` and information about whether - /// the local peer is a server or a client. + /// next incoming changeset from P. /// /// In general, if A and B are two causally unrelated (alternative) /// changesets based on the same version V, then the operational @@ -293,58 +177,95 @@ class Transformer { /// only when carried out between alternative changesets based on the same /// version. /// + /// \param local_file_ident The identifier of the local Realm file. The + /// transformer uses this as the actual origin file identifier for + /// changesets where HistoryEntry::origin_file_ident is zero, i.e., when the + /// changeset is of local origin. The specified identifier must never be + /// zero. + /// /// \return The size of the transformed version of the specified - /// changeset. Upon return, the changeset itself is stored in the specified - /// output buffer. + /// changesets. Upon return, the transformed changesets are concatenated + /// and placed in \a output_buffer. /// /// \throw TransformError Thrown if operational transformation fails due to /// a problem with the specified changeset. - virtual size_t transform_remote_changeset(TransformHistory&, - version_type current_local_version, - RemoteChangeset changeset, - util::Buffer& output_buffer) = 0; + /// + /// FIXME: Consider using std::error_code instead of throwing + /// TransformError. + virtual void transform_remote_changesets(TransformHistory&, + file_ident_type local_file_ident, + version_type current_local_version, + Changeset* changesets, + std::size_t num_changesets, + Reporter* = nullptr) = 0; virtual ~Transformer() noexcept {} }; +std::unique_ptr make_transformer(); + + + +class Transformer::RemoteChangeset { +public: + /// The version produced on the remote peer by this changeset. + /// + /// On the server, the remote peer is the client from which the changeset + /// originated, and `remote_version` is the client version produced by the + /// changeset on that client. + /// + /// On a client, the remote peer is the server, and `remote_version` is the + /// server version produced by this changeset on the server. Since the + /// server is never the originator of changes, this changeset must in turn + /// have been produced on the server by integration of a changeset uploaded + /// by some other client. + version_type remote_version = 0; + + /// The last local version that has been integrated into `remote_version`. + /// + /// A local version, L, has been integrated into a remote version, R, when, + /// and only when L is the latest local version such that all preceeding + /// changesets in the local history have been integrated by the remote peer + /// prior to R. + /// + /// On the server, this is the last server version integrated into the + /// client version `remote_version`. On a client, it is the last client + /// version integrated into the server version `remote_version`. + version_type last_integrated_local_version = 0; + + /// The changeset itself. + ChunkedBinaryData data; + + /// Same meaning as `HistoryEntry::origin_timestamp`. + timestamp_type origin_timestamp = 0; + + /// Same meaning as `HistoryEntry::origin_file_ident`. + file_ident_type origin_file_ident = 0; + + /// If the changeset was compacted during download, the size of the original + /// changeset. + size_t original_changeset_size = 0; + + RemoteChangeset() {} + RemoteChangeset(version_type rv, version_type lv, ChunkedBinaryData d, timestamp_type ot, + file_ident_type fi); +}; + -/// \param local_client_file_ident The server assigned local client file -/// identifier. This must be zero on the server-side, and only on the -/// server-side. -std::unique_ptr make_transformer(Transformer::file_ident_type local_client_file_ident); +class Transformer::Reporter { +public: + virtual void on_changesets_merged(long num_merges) = 0; +}; -// Implementation +void parse_remote_changeset(const Transformer::RemoteChangeset&, Changeset&); -inline bool TransformHistory::register_local_time(timestamp_type local_timestamp, - timestamp_type& timestamp_threshold) noexcept -{ - // Needed to ensure causal consistency. This also guards against - // nonmonotonic local time. - if (timestamp_threshold < local_timestamp) { - timestamp_threshold = local_timestamp; - return true; - } - return false; -} -inline bool TransformHistory::register_remote_time(timestamp_type remote_timestamp, - timestamp_type& timestamp_threshold) noexcept -{ - // To ensure causal consistency, we need to know the latest remote (or - // local) timestamp seen so far. Adding one to the incoming remote - // timestamp, before using it to bump the `timestamp_threshold`, is a - // simple way of ensuring not only proper ordering among timestamps, but - // also among 'effective timestamps' (which is required), regardless of the - // values of the assiciated client file identifiers. - if (timestamp_threshold < remote_timestamp + 1) { - timestamp_threshold = remote_timestamp + 1; - return true; - } - return false; -} + + +// Implementation class TransformError: public std::runtime_error { public: @@ -355,13 +276,13 @@ class TransformError: public std::runtime_error { }; inline Transformer::RemoteChangeset::RemoteChangeset(version_type rv, version_type lv, - BinaryData d, timestamp_type ot, + ChunkedBinaryData d, timestamp_type ot, file_ident_type fi): remote_version(rv), last_integrated_local_version(lv), data(d), origin_timestamp(ot), - origin_client_file_ident(fi) + origin_file_ident(fi) { } diff --git a/Pods/Realm/include/core/realm/sync/version.hpp b/Pods/Realm/include/core/realm/sync/version.hpp index 3f65140..2ad11ee 100644 --- a/Pods/Realm/include/core/realm/sync/version.hpp +++ b/Pods/Realm/include/core/realm/sync/version.hpp @@ -22,9 +22,9 @@ #include -#define REALM_SYNC_VER_MAJOR 1 -#define REALM_SYNC_VER_MINOR 1 -#define REALM_SYNC_VER_PATCH 0 +#define REALM_SYNC_VER_MAJOR 3 +#define REALM_SYNC_VER_MINOR 8 +#define REALM_SYNC_VER_PATCH 8 #define REALM_SYNC_PRODUCT_NAME "realm-sync" #define REALM_SYNC_VER_STRING REALM_QUOTE(REALM_SYNC_VER_MAJOR) "." \ diff --git a/Pods/Realm/include/core/realm/table.hpp b/Pods/Realm/include/core/realm/table.hpp index 01c44ac..d4833cb 100644 --- a/Pods/Realm/include/core/realm/table.hpp +++ b/Pods/Realm/include/core/realm/table.hpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -35,10 +36,13 @@ #include #include #include +#include namespace realm { class BacklinkColumn; +template +class BacklinkCount; class BinaryColumy; class ConstTableView; class Group; @@ -57,6 +61,8 @@ template class SubQuery; struct LinkTargetInfo; +struct SubTable { +}; struct Link { }; typedef Link LinkList; @@ -65,18 +71,13 @@ typedef Link BackLink; namespace _impl { class TableFriend; } +namespace metrics { +class QueryInfo; +} class Replication; -/// The Table class is non-polymorphic, that is, it has no virtual -/// functions. This is important because it ensures that there is no run-time -/// distinction between a Table instance and an instance of any variation of -/// BasicTable, and this, in turn, makes it valid to cast a pointer from -/// Table to BasicTable even when the instance is constructed as a Table. Of -/// course, this also assumes that BasicTable<> is non-polymorphic, has no -/// destructor, and adds no extra data members. -/// /// FIXME: Table assignment (from any group to any group) could be made aliasing /// safe as follows: Start by cloning source table into target allocator. On /// success, assign, and then deallocate any previous structure at the target. @@ -165,6 +166,10 @@ class Table { // Whether or not elements can be null. bool is_nullable(size_t col_ndx) const; + // Returns the link type for the given column. + // Throws an LogicError if target column is not a link column. + LinkType get_link_type(size_t col_ndx) const; + //@{ /// Conventience functions for inspecting the dynamic table type. /// @@ -174,6 +179,8 @@ class Table { DataType get_column_type(size_t column_ndx) const noexcept; StringData get_column_name(size_t column_ndx) const noexcept; size_t get_column_index(StringData name) const noexcept; + typedef util::Optional> BacklinkOrigin; + BacklinkOrigin find_backlink_origin(StringData origin_table_name, StringData origin_col_name) const noexcept; //@} //@{ @@ -219,28 +226,28 @@ class Table { void remove_column(size_t column_ndx); void rename_column(size_t column_ndx, StringData new_name); //@} - //@{ /// has_search_index() returns true if, and only if a search index has been /// added to the specified column. Rather than throwing, it returns false if /// the table accessor is detached or the specified index is out of range. /// - /// add_search_index() adds a search index to the specified column of this + /// add_search_index() adds a search index to the specified column of the /// table. It has no effect if a search index has already been added to the /// specified column (idempotency). /// /// remove_search_index() removes the search index from the specified column - /// of this table. It has no effect if the specified column has no search + /// of the table. It has no effect if the specified column has no search /// index. The search index cannot be removed from the primary key of a /// table. /// /// This table must be a root table; that is, it must have an independent /// descriptor. Freestanding tables, group-level tables, and subtables in a /// column of type 'mixed' are all examples of root tables. See add_column() - /// for more on this. + /// for more on this. If you want to manipulate subtable indexes, you must use + /// the Descriptor interface. /// - /// \param column_ndx The index of a column of this table. + /// \param column_ndx The index of a column of the table. bool has_search_index(size_t column_ndx) const noexcept; void add_search_index(size_t column_ndx); @@ -333,6 +340,9 @@ class Table { Columns column(size_t column); // FIXME: Should this one have been declared noexcept? template Columns column(const Table& origin, size_t origin_column_ndx); + // BacklinkCount is a total count per row and therefore not attached to a specific column + template + BacklinkCount get_backlink_count(); template SubQuery column(size_t column, Query subquery); @@ -373,6 +383,9 @@ class Table { /// vacated slot. This operation assumes that the table is unordered, and it /// may therfore be used on tables with link columns. /// + /// remove_recursive() will delete linked rows if the removed link was the + /// last one holding on to the row in question. This will be done recursively. + /// /// The removal of a row from an unordered table (move_last_over()) may /// cause other linked rows to be cascade-removed. The clearing of a table /// may also cause linked rows to be cascade-removed, but in this respect, @@ -381,11 +394,15 @@ class Table { size_t add_empty_row(size_t num_rows = 1); void insert_empty_row(size_t row_ndx, size_t num_rows = 1); + size_t add_row_with_key(size_t col_ndx, util::Optional key); + size_t add_row_with_keys(size_t col_1_ndx, int64_t key1, size_t col_2_ndx, StringData key2); void remove(size_t row_ndx); + void remove_recursive(size_t row_ndx); void remove_last(); void move_last_over(size_t row_ndx); void clear(); void swap_rows(size_t row_ndx_1, size_t row_ndx_2); + void move_row(size_t from_ndx, size_t to_ndx); //@} /// Replaces all links to \a row_ndx with links to \a new_row_ndx. @@ -400,7 +417,22 @@ class Table { /// \sa Table::set_null_unique() void merge_rows(size_t row_ndx, size_t new_row_ndx); - // Get cell values. Will assert if the requested type does not match the column type + //@{ + + /// Get cell values. + /// Will assert if the requested type does not match the column type. + /// + /// When fetching from a nullable column and the value is null, a default + /// value will be returned, except for object like types (StringData, + /// BinaryData, Timestamp) which have support for storing nulls. In that + /// case, call the `is_null()` method on the returned object to check + /// whether the stored value was null. If nullability matters and returning + /// a default value is unacceptable, check Table::is_null() before getting a + /// cell value. + /// + /// \sa Table::is_nullable(size_t col_ndx) + /// \sa Table::is_null(size_t col_ndx, size_t row_ndx) + /// \sa StringData::is_null() int64_t get_int(size_t column_ndx, size_t row_ndx) const noexcept; bool get_bool(size_t column_ndx, size_t row_ndx) const noexcept; OldDateTime get_olddatetime(size_t column_ndx, size_t row_ndx) const noexcept; @@ -408,10 +440,19 @@ class Table { double get_double(size_t column_ndx, size_t row_ndx) const noexcept; StringData get_string(size_t column_ndx, size_t row_ndx) const noexcept; BinaryData get_binary(size_t column_ndx, size_t row_ndx) const noexcept; + BinaryIterator get_binary_iterator(size_t column_ndx, size_t row_ndx) const noexcept; Mixed get_mixed(size_t column_ndx, size_t row_ndx) const noexcept; DataType get_mixed_type(size_t column_ndx, size_t row_ndx) const noexcept; Timestamp get_timestamp(size_t column_ndx, size_t row_ndx) const noexcept; + //@} + + /// Return data from position 'pos' and onwards. If the blob is distributed + /// across multiple arrays, you will only get data from one array. 'pos' + /// will be updated to be an index to next available data. It will be 0 + /// if no more data. + BinaryData get_binary_at(size_t col_ndx, size_t ndx, size_t& pos) const noexcept; + template T get(size_t c, size_t r) const noexcept; @@ -426,9 +467,6 @@ class Table { TableRef get_link_target(size_t column_ndx) noexcept; ConstTableRef get_link_target(size_t column_ndx) const noexcept; - template - typename T::RowAccessor get_link_accessor(size_t column_ndx, size_t row_ndx); - //@{ /// Set cell values. @@ -483,7 +521,7 @@ class Table { /// /// String level modifications performed via insert_substring() and /// remove_substring() are mergable and subject to operational - /// trsnaformation. That is, the effect of two causally unrelated + /// transformation. That is, the effect of two causally unrelated /// modifications will in general both be retained during synchronization. static const size_t max_string_size = 0xFFFFF8 - Array::header_size - 1; @@ -496,6 +534,12 @@ class Table { static constexpr int_fast64_t max_integer = std::numeric_limits::max(); static constexpr int_fast64_t min_integer = std::numeric_limits::min(); + template + void set(size_t c, size_t r, T value, bool is_default = false); + + template + size_t set_unique(size_t c, size_t r, T value); + void set_int(size_t column_ndx, size_t row_ndx, int_fast64_t value, bool is_default = false); size_t set_int_unique(size_t column_ndx, size_t row_ndx, int_fast64_t value); void set_bool(size_t column_ndx, size_t row_ndx, bool value, bool is_default = false); @@ -514,6 +558,11 @@ class Table { void set_null(size_t column_ndx, size_t row_ndx, bool is_default = false); void set_null_unique(size_t col_ndx, size_t row_ndx); + // Sync needs to store blobs bigger than 16 M. This function can be used for that. Data should be read + // out again using the get_binary_at() function. Should not be used for user data as normal get_binary() + // will just return null if the data is bigger than the limit. + void set_binary_big(size_t column_ndx, size_t row_ndx, BinaryData value, bool is_default = false); + void add_int(size_t column_ndx, size_t row_ndx, int_fast64_t value); void insert_substring(size_t col_ndx, size_t row_ndx, size_t pos, StringData); @@ -535,12 +584,14 @@ class Table { // 'mixed', for a value in a mixed column that is not a subtable, // get_subtable() returns null, get_subtable_size() returns zero, // and clear_subtable() replaces the value with an empty table.) + // Currently, subtables of subtables are not supported. TableRef get_subtable(size_t column_ndx, size_t row_ndx); ConstTableRef get_subtable(size_t column_ndx, size_t row_ndx) const; size_t get_subtable_size(size_t column_ndx, size_t row_ndx) const noexcept; void clear_subtable(size_t column_ndx, size_t row_ndx); // Backlinks + size_t get_backlink_count(size_t row_ndx, bool only_strong_links = false) const noexcept; size_t get_backlink_count(size_t row_ndx, const Table& origin, size_t origin_col_ndx) const noexcept; size_t get_backlink(size_t row_ndx, const Table& origin, size_t origin_col_ndx, size_t backlink_ndx) const noexcept; @@ -602,6 +653,9 @@ class Table { double average_double(size_t column_ndx, size_t* value_count = nullptr) const; // Searching + template + size_t find_first(size_t column_ndx, T value) const; + size_t find_first_link(size_t target_row_index) const; size_t find_first_int(size_t column_ndx, int64_t value) const; size_t find_first_bool(size_t column_ndx, bool value) const; @@ -673,8 +727,6 @@ class Table { uint_fast64_t get_version_counter() const noexcept; private: - template - size_t find_first(size_t column_ndx, T value) const; // called by above methods template TableView find_all(size_t column_ndx, T value); @@ -740,8 +792,33 @@ class Table { return Query(*this, lv); } - Table& link(size_t link_column); + //@{ + /// WARNING: The link() and backlink() methods will alter a state on the Table object and return a reference to itself. + /// Be aware if assigning the return value of link() to a variable; this might be an error! + + /// This is an error: + + /// Table& cats = owners->link(1); + /// auto& dogs = owners->link(2); + + /// Query q = person_table->where() + /// .and_query(cats.column(5).equal("Fido")) + /// .Or() + /// .and_query(dogs.column(6).equal("Meowth")); + + /// Instead, do this: + + /// Query q = owners->where() + /// .and_query(person_table->link(1).column(5).equal("Fido")) + /// .Or() + /// .and_query(person_table->link(2).column(6).equal("Meowth")); + + /// The two calls to link() in the errorneous example will append the two values 0 and 1 to an internal vector in the + /// owners table, and we end up with three references to that same table: owners, cats and dogs. They are all the same + /// table, its vector has the values {0, 1}, so a query would not make any sense. + Table& link(size_t link_column); Table& backlink(const Table& origin, size_t origin_col_ndx); + //@} // Optimizing. enforce == true will enforce enumeration of all string columns; // enforce == false will auto-evaluate if they should be enumerated or not @@ -828,7 +905,15 @@ class Table { /// where it takes up a minimal amout of space. This function returns true /// if, and only if the table accessor is attached to such a subtable. This /// function is mainly intended for debugging purposes. - bool is_degenerate() const noexcept; + bool is_degenerate() const; + + /// Compute the sum of the sizes in number of bytes of all the array nodes + /// that currently make up this table. See also + /// Group::compute_aggregate_byte_size(). + /// + /// If this table accessor is the detached state, this function returns + /// zero. + size_t compute_aggregated_byte_size() const noexcept; // Debug void verify() const; @@ -851,10 +936,10 @@ class Table { /// /// The returned table pointer must **always** end up being /// wrapped in some instantiation of BasicTableRef<>. - Table* get_subtable_ptr(size_t col_ndx, size_t row_ndx); + TableRef get_subtable_tableref(size_t col_ndx, size_t row_ndx); - /// See non-const get_subtable_ptr(). - const Table* get_subtable_ptr(size_t col_ndx, size_t row_ndx) const; + /// See non-const get_subtable_tableref(). + ConstTableRef get_subtable_tableref(size_t col_ndx, size_t row_ndx) const; /// Compare the rows of two tables under the assumption that the two tables /// have the same number of columns, and the same data type at each column @@ -899,7 +984,70 @@ class Table { // degenerate state in a different way. Array m_top; Array m_columns; // 2nd slot in m_top (for root tables) - Spec m_spec; // 1st slot in m_top (for root tables) + + // Management class for the spec object. Only if the table has an independent + // spec, the spec object should be deleted when the table object is deleted. + // If the table has a shared spec, the spec object is managed by the spec object + // of the containing table. + class SpecPtr { + public: + ~SpecPtr() + { + optionally_delete(); + } + void manage(Spec* ptr) + { + optionally_delete(); + m_p = ptr; + m_is_managed = true; + } + void detach() + { + if (m_is_managed) { + m_p->detach(); + } + } + SpecPtr& operator=(Spec* ptr) + { + optionally_delete(); + m_p = ptr; + m_is_managed = false; + return *this; + } + Spec* operator->() const + { + return m_p; + } + Spec* get() const + { + return m_p; + } + Spec& operator*() const + { + return *m_p; + } + operator bool() const + { + return m_p != nullptr; + } + bool is_managed() const + { + return m_is_managed; + } + + private: + Spec* m_p = nullptr; + bool m_is_managed = false; + + void optionally_delete() + { + if (m_is_managed) { + delete m_p; + } + } + }; + + SpecPtr m_spec; // 1st slot in m_top (for root tables) // Is guaranteed to be empty for a detached accessor. Otherwise it is empty // when the table accessor is attached to a degenerate subtable (unattached @@ -952,6 +1100,7 @@ class Table { void do_remove(size_t row_ndx, bool broken_reciprocal_backlinks); void do_move_last_over(size_t row_ndx, bool broken_reciprocal_backlinks); void do_swap_rows(size_t row_ndx_1, size_t row_ndx_2); + void do_move_row(size_t from_ndx, size_t to_ndx); void do_merge_rows(size_t row_ndx, size_t new_row_ndx); void do_clear(bool broken_reciprocal_backlinks); size_t do_set_link(size_t col_ndx, size_t row_ndx, size_t target_row_ndx); @@ -962,11 +1111,20 @@ class Table { template size_t do_set_unique(ColType& column, size_t row_ndx, T&& value, bool& conflict); - void upgrade_file_format(size_t target_file_format_version); + void _add_search_index(size_t column_ndx); + void _remove_search_index(size_t column_ndx); + + void rebuild_search_index(size_t current_file_format_version); // Upgrades OldDateTime columns to Timestamp columns void upgrade_olddatetime(); + // Indicate that the current global state version has been "observed". Until this + // happens, bumping of the global version counter can be bypassed, as any query + // checking for a version change will see the older version change anyways. + // Also returns the table-local version. + uint64_t observe_version() const noexcept; + /// Update the version of this table and all tables which have links to it. /// This causes all views referring to those tables to go out of sync, so that /// calls to sync_if_needed() will bring the view up to date by reexecuting the @@ -995,7 +1153,7 @@ class Table { /// assign() would not check for spec compatibility. This would /// make it ideal as a basis for implementing operator=() for /// typed tables. - Table& operator=(const Table&); + Table& operator=(const Table&) = delete; /// Used when constructing an accessor whose lifetime is going to be managed /// by reference counting. The lifetime of accessors of free-standing tables @@ -1009,7 +1167,7 @@ class Table { Table(ref_count_tag, Allocator&); void init(ref_type top_ref, ArrayParent*, size_t ndx_in_parent, bool skip_create_column_accessors = false); - void init(ConstSubspecRef shared_spec, ArrayParent* parent_column, size_t parent_row_ndx); + void init(Spec* shared_spec, ArrayParent* parent_column, size_t parent_row_ndx); static void do_insert_column(Descriptor&, size_t col_ndx, DataType type, StringData name, LinkTargetInfo& link_target_info, bool nullable = false); @@ -1018,20 +1176,19 @@ class Table { bool* was_inserted = nullptr); static void do_erase_column(Descriptor&, size_t col_ndx); static void do_rename_column(Descriptor&, size_t col_ndx, StringData name); - static void do_move_column(Descriptor&, size_t col_ndx_1, size_t col_ndx_2); + + static void do_add_search_index(Descriptor&, size_t col_ndx); + static void do_remove_search_index(Descriptor&, size_t col_ndx); struct InsertSubtableColumns; struct EraseSubtableColumns; struct RenameSubtableColumns; - struct MoveSubtableColumns; void insert_root_column(size_t col_ndx, DataType type, StringData name, LinkTargetInfo& link_target, bool nullable = false); void erase_root_column(size_t col_ndx); - void move_root_column(size_t from, size_t to); void do_insert_root_column(size_t col_ndx, ColumnType, StringData name, bool nullable = false); void do_erase_root_column(size_t col_ndx); - void do_move_root_column(size_t from, size_t to); void do_set_link_type(size_t col_ndx, LinkType); void insert_backlink_column(size_t origin_table_ndx, size_t origin_col_ndx, size_t backlink_col_ndx); void erase_backlink_column(size_t origin_table_ndx, size_t origin_col_ndx); @@ -1172,6 +1329,8 @@ class Table { const BacklinkColumn& get_column_backlink(size_t ndx) const noexcept; BacklinkColumn& get_column_backlink(size_t ndx); + void verify_column(size_t col_ndx, const ColumnBase* col) const; + void instantiate_before_change(); void validate_column_type(const ColumnBase& col, ColumnType expected_type, size_t ndx) const; @@ -1207,8 +1366,6 @@ class Table { void connect_opposite_link_columns(size_t link_col_ndx, Table& target_table, size_t backlink_col_ndx) noexcept; - size_t get_num_strong_backlinks(size_t row_ndx) const noexcept; - //@{ /// Cascading removal of strong links. @@ -1302,7 +1459,7 @@ class Table { /// that the specified column index in a valid index into `m_cols` but does /// not otherwise assume more than minimal accessor consistency (see /// AccessorConsistencyLevels.) - Table* get_subtable_accessor(size_t col_ndx, size_t row_ndx) noexcept; + TableRef get_subtable_accessor(size_t col_ndx, size_t row_ndx) noexcept; /// Unless the column accessor is missing, this function returns the /// accessor for the target table of the specified link-type column. The @@ -1321,6 +1478,7 @@ class Table { void adj_acc_insert_rows(size_t row_ndx, size_t num_rows) noexcept; void adj_acc_erase_row(size_t row_ndx) noexcept; void adj_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept; + void adj_acc_move_row(size_t from_ndx, size_t to_ndx) noexcept; void adj_acc_merge_rows(size_t old_row_ndx, size_t new_row_ndx) noexcept; /// Adjust this table accessor and its subordinates after move_last_over() @@ -1361,6 +1519,7 @@ class Table { void adj_row_acc_insert_rows(size_t row_ndx, size_t num_rows) noexcept; void adj_row_acc_erase_row(size_t row_ndx) noexcept; void adj_row_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept; + void adj_row_acc_move_row(size_t from_ndx, size_t to_ndx) noexcept; void adj_row_acc_merge_rows(size_t old_row_ndx, size_t new_row_ndx) noexcept; /// Called by adj_acc_move_over() to adjust row accessors. @@ -1368,7 +1527,6 @@ class Table { void adj_insert_column(size_t col_ndx); void adj_erase_column(size_t col_ndx) noexcept; - void adj_move_column(size_t col_ndx_1, size_t col_ndx_2) noexcept; bool is_marked() const noexcept; void mark() noexcept; @@ -1416,6 +1574,8 @@ class Table { /// root ref is stored in the parent (see AccessorConsistencyLevels). void refresh_accessor_tree(); + void refresh_spec_accessor(); + void refresh_column_accessors(size_t col_ndx_begin = 0); // Look for link columns starting from col_ndx_begin. @@ -1424,7 +1584,7 @@ class Table { void refresh_link_target_accessors(size_t col_ndx_begin = 0); bool is_cross_table_link_target() const noexcept; - + std::recursive_mutex* get_parent_accessor_management_lock() const; #ifdef REALM_DEBUG void to_dot_internal(std::ostream&) const; #endif @@ -1432,6 +1592,7 @@ class Table { friend class SubtableNode; friend class _impl::TableFriend; friend class Query; + friend class metrics::QueryInfo; template friend class util::bind_ptr; template @@ -1444,6 +1605,7 @@ class Table { friend class ParentNode; template friend class SequentialGetter; + friend struct util::serializer::SerialisationState; friend class RowBase; friend class LinksToNode; friend class LinkMap; @@ -1451,7 +1613,6 @@ class Table { friend class Group; }; - class Table::Parent : public ArrayParent { public: ~Parent() noexcept override @@ -1473,6 +1634,8 @@ class Table::Parent : public ArrayParent { /// when this table parent is a column in a parent table. virtual Table* get_parent_table(size_t* column_ndx_out = nullptr) noexcept; + virtual Spec* get_subtable_spec() noexcept; + /// Must be called whenever a child table accessor is about to be destroyed. /// /// Note that the argument is a pointer to the child Table rather than its @@ -1480,7 +1643,9 @@ class Table::Parent : public ArrayParent { /// consistency can be assumed by this function. virtual void child_accessor_destroyed(Table* child) noexcept = 0; + virtual size_t* record_subtable_path(size_t* begin, size_t* end) noexcept; + virtual std::recursive_mutex* get_accessor_management_lock() noexcept = 0; friend class Table; }; @@ -1491,6 +1656,12 @@ class Table::Parent : public ArrayParent { inline uint_fast64_t Table::get_version_counter() const noexcept { + return observe_version(); +} + +inline uint64_t Table::observe_version() const noexcept +{ + m_top.get_alloc().observe_version(); return m_version; } @@ -1552,11 +1723,21 @@ inline void Table::unbind_ptr() const noexcept // any changes, so it is a convenient place to do a release. // The release will then be observed by the acquire fence in // the case where delete is actually called (the count reaches 0) - if (m_ref_count.fetch_sub(1, std::memory_order_release) != 1) + if (m_ref_count.fetch_sub(1, std::memory_order_release) != 1) { return; + } std::atomic_thread_fence(std::memory_order_acquire); - delete this; + + std::recursive_mutex* lock = get_parent_accessor_management_lock(); + if (lock) { + std::lock_guard lg(*lock); + if (m_ref_count == 0) + delete this; + } + else { + delete this; + } } inline void Table::register_view(const TableViewBase* view) @@ -1597,31 +1778,31 @@ inline StringData Table::get_name() const noexcept inline size_t Table::get_column_count() const noexcept { REALM_ASSERT(is_attached()); - return m_spec.get_public_column_count(); + return m_spec->get_public_column_count(); } inline StringData Table::get_column_name(size_t ndx) const noexcept { REALM_ASSERT_3(ndx, <, get_column_count()); - return m_spec.get_column_name(ndx); + return m_spec->get_column_name(ndx); } inline size_t Table::get_column_index(StringData name) const noexcept { REALM_ASSERT(is_attached()); - return m_spec.get_column_index(name); + return m_spec->get_column_index(name); } inline ColumnType Table::get_real_column_type(size_t ndx) const noexcept { - REALM_ASSERT_3(ndx, <, m_spec.get_column_count()); - return m_spec.get_column_type(ndx); + REALM_ASSERT_3(ndx, <, m_spec->get_column_count()); + return m_spec->get_column_type(ndx); } inline DataType Table::get_column_type(size_t ndx) const noexcept { - REALM_ASSERT_3(ndx, <, m_spec.get_column_count()); - return m_spec.get_public_column_type(ndx); + REALM_ASSERT_3(ndx, <, m_spec->get_column_count()); + return m_spec->get_public_column_type(ndx); } template @@ -1652,6 +1833,18 @@ inline bool Table::has_shared_type() const noexcept return !m_top.is_attached(); } +inline void Table::verify_column(size_t col_ndx, const ColumnBase* col) const +{ + // Check if the column exists at the expected location + if (REALM_LIKELY(col_ndx < m_cols.size() && m_cols[col_ndx] == col)) + return; + // The column might be elsewhere in the list + for (auto c : m_cols) { + if (c == col) + return; + } + throw LogicError(LogicError::column_does_not_exist); +} class Table::UnbindGuard { public: @@ -1696,9 +1889,8 @@ class Table::UnbindGuard { inline Table::Table(Allocator& alloc) : m_top(alloc) , m_columns(alloc) - , m_spec(alloc) { - m_ref_count = 1; // Explicitely managed lifetime + m_ref_count = 1; // Explicitly managed lifetime ref_type ref = create_empty_table(alloc); // Throws Parent* parent = nullptr; @@ -1709,9 +1901,8 @@ inline Table::Table(Allocator& alloc) inline Table::Table(const Table& t, Allocator& alloc) : m_top(alloc) , m_columns(alloc) - , m_spec(alloc) { - m_ref_count = 1; // Explicitely managed lifetime + m_ref_count = 1; // Explicitly managed lifetime ref_type ref = t.clone(alloc); // Throws Parent* parent = nullptr; @@ -1722,7 +1913,6 @@ inline Table::Table(const Table& t, Allocator& alloc) inline Table::Table(ref_count_tag, Allocator& alloc) : m_top(alloc) , m_columns(alloc) - , m_spec(alloc) { m_ref_count = 0; // Lifetime managed by reference counting } @@ -1765,15 +1955,15 @@ inline Columns Table::column(size_t column_ndx) realm::DataType ct = table->get_column_type(column_ndx); if (std::is_same::value && ct != type_Int) - throw(LogicError::type_mismatch); + throw LogicError(LogicError::type_mismatch); else if (std::is_same::value && ct != type_Bool) - throw(LogicError::type_mismatch); + throw LogicError(LogicError::type_mismatch); else if (std::is_same::value && ct != type_OldDateTime) - throw(LogicError::type_mismatch); + throw LogicError(LogicError::type_mismatch); else if (std::is_same::value && ct != type_Float) - throw(LogicError::type_mismatch); + throw LogicError(LogicError::type_mismatch); else if (std::is_same::value && ct != type_Double) - throw(LogicError::type_mismatch); + throw LogicError(LogicError::type_mismatch); if (std::is_same::value || std::is_same::value || std::is_same::value) { link_chain.push_back(column_ndx); @@ -1789,7 +1979,7 @@ inline Columns Table::column(const Table& origin, size_t origin_col_ndx) size_t origin_table_ndx = origin.get_index_in_group(); const Table& current_target_table = *get_link_chain_target(m_link_chain); - size_t backlink_col_ndx = current_target_table.m_spec.find_backlink_column(origin_table_ndx, origin_col_ndx); + size_t backlink_col_ndx = current_target_table.m_spec->find_backlink_column(origin_table_ndx, origin_col_ndx); std::vector link_chain = std::move(m_link_chain); m_link_chain.clear(); @@ -1798,6 +1988,14 @@ inline Columns Table::column(const Table& origin, size_t origin_col_ndx) return Columns(backlink_col_ndx, this, std::move(link_chain)); } +template +inline BacklinkCount Table::get_backlink_count() +{ + std::vector link_chain = std::move(m_link_chain); + m_link_chain.clear(); + return BacklinkCount(this, std::move(link_chain)); +} + template SubQuery Table::column(size_t column_ndx, Query subquery) { @@ -1812,7 +2010,6 @@ SubQuery Table::column(const Table& origin, size_t origin_col_ndx, Query subq return SubQuery(column(origin, origin_col_ndx), std::move(subquery)); } -// For use by queries inline Table& Table::link(size_t link_column) { m_link_chain.push_back(link_column); @@ -1823,7 +2020,7 @@ inline Table& Table::backlink(const Table& origin, size_t origin_col_ndx) { size_t origin_table_ndx = origin.get_index_in_group(); const Table& current_target_table = *get_link_chain_target(m_link_chain); - size_t backlink_col_ndx = current_target_table.m_spec.find_backlink_column(origin_table_ndx, origin_col_ndx); + size_t backlink_col_ndx = current_target_table.m_spec->find_backlink_column(origin_table_ndx, origin_col_ndx); return link(backlink_col_ndx); } @@ -1886,9 +2083,9 @@ inline size_t Table::add_empty_row(size_t num_rows) return row_ndx; // Return index of first new row } -inline const Table* Table::get_subtable_ptr(size_t col_ndx, size_t row_ndx) const +inline ConstTableRef Table::get_subtable_tableref(size_t col_ndx, size_t row_ndx) const { - return const_cast(this)->get_subtable_ptr(col_ndx, row_ndx); // Throws + return const_cast(this)->get_subtable_tableref(col_ndx, row_ndx); // Throws } inline bool Table::is_null_link(size_t col_ndx, size_t row_ndx) const noexcept @@ -1914,12 +2111,12 @@ inline void Table::nullify_link(size_t col_ndx, size_t row_ndx) inline TableRef Table::get_subtable(size_t column_ndx, size_t row_ndx) { - return TableRef(get_subtable_ptr(column_ndx, row_ndx)); + return get_subtable_tableref(column_ndx, row_ndx); } inline ConstTableRef Table::get_subtable(size_t column_ndx, size_t row_ndx) const { - return ConstTableRef(get_subtable_ptr(column_ndx, row_ndx)); + return get_subtable_tableref(column_ndx, row_ndx); } inline ConstTableRef Table::get_parent_table(size_t* column_ndx_out) const noexcept @@ -1939,7 +2136,7 @@ inline bool Table::is_group_level() const noexcept inline bool Table::operator==(const Table& t) const { - return m_spec == t.m_spec && compare_rows(t); // Throws + return *m_spec == *t.m_spec && compare_rows(t); // Throws } inline bool Table::operator!=(const Table& t) const @@ -1947,8 +2144,12 @@ inline bool Table::operator!=(const Table& t) const return !(*this == t); // Throws } -inline bool Table::is_degenerate() const noexcept +inline bool Table::is_degenerate() const { + if (!is_attached()) { + throw LogicError{LogicError::detached_accessor}; + } + return !m_columns.is_attached(); } @@ -1993,17 +2194,6 @@ inline size_t* Table::Parent::record_subtable_path(size_t* begin, size_t*) noexc return begin; } -template -typename T::RowAccessor Table::get_link_accessor(size_t column_ndx, size_t row_ndx) -{ - size_t row_pos_in_target = get_link(column_ndx, row_ndx); - TableRef target_table = get_link_target(column_ndx); - - Table* table = &*target_table; - T* typed_table = reinterpret_cast(table); - return (*typed_table)[row_pos_in_target]; -} - inline bool Table::is_marked() const noexcept { return m_mark; @@ -2036,6 +2226,163 @@ inline void Table::set_ndx_in_parent(size_t ndx_in_parent) noexcept } } +// Declare our explicit specializations so that the inline wrappers don't try +// to instantiate them +template<> int64_t Table::get(size_t, size_t) const noexcept; +template<> util::Optional Table::get>(size_t, size_t) const noexcept; +template<> bool Table::get(size_t, size_t) const noexcept; +template<> Optional Table::get>(size_t, size_t) const noexcept; +template<> float Table::get(size_t, size_t) const noexcept; +template<> util::Optional Table::get>(size_t, size_t) const noexcept; +template<> double Table::get(size_t, size_t) const noexcept; +template<> util::Optional Table::get>(size_t, size_t) const noexcept; +template<> OldDateTime Table::get(size_t, size_t) const noexcept; +template<> Timestamp Table::get(size_t, size_t) const noexcept; +template<> StringData Table::get(size_t, size_t) const noexcept; +template<> BinaryData Table::get(size_t, size_t) const noexcept; +template<> BinaryIterator Table::get(size_t, size_t) const noexcept; +template<> Mixed Table::get(size_t, size_t) const noexcept; + +template<> void Table::set(size_t, size_t, int64_t, bool); +template<> void Table::set(size_t, size_t, bool, bool); +template<> void Table::set(size_t, size_t, float, bool); +template<> void Table::set(size_t, size_t, double, bool); +template<> void Table::set(size_t, size_t, OldDateTime, bool); +template<> void Table::set(size_t, size_t, Timestamp, bool); +template<> void Table::set(size_t, size_t, StringData, bool); +template<> void Table::set(size_t, size_t, BinaryData, bool); +template<> void Table::set(size_t, size_t, Mixed, bool); +template<> void Table::set(size_t, size_t, null, bool); + +template<> size_t Table::set_unique(size_t, size_t, int64_t); +template<> size_t Table::set_unique(size_t, size_t, StringData); +template<> size_t Table::set_unique(size_t, size_t, null); + + +inline int64_t Table::get_int(size_t col_ndx, size_t ndx) const noexcept +{ + if (is_nullable(col_ndx)) + return get>(col_ndx, ndx).value_or(0); + else + return get(col_ndx, ndx); +} + +inline size_t Table::set_int_unique(size_t col_ndx, size_t ndx, int_fast64_t value) +{ + return set_unique(col_ndx, ndx, value); +} + +inline void Table::set_int(size_t col_ndx, size_t ndx, int_fast64_t value, bool is_default) +{ + return set(col_ndx, ndx, value, is_default); +} + +inline Timestamp Table::get_timestamp(size_t col_ndx, size_t ndx) const noexcept +{ + return get(col_ndx, ndx); +} + +inline void Table::set_timestamp(size_t col_ndx, size_t ndx, Timestamp value, bool is_default) +{ + return set(col_ndx, ndx, value, is_default); +} + +inline bool Table::get_bool(size_t col_ndx, size_t ndx) const noexcept +{ + if (is_nullable(col_ndx)) + return get>(col_ndx, ndx).value_or(false); + else + return get(col_ndx, ndx); +} + +inline void Table::set_bool(size_t col_ndx, size_t ndx, bool value, bool is_default) +{ + return set(col_ndx, ndx, value, is_default); +} + +inline OldDateTime Table::get_olddatetime(size_t col_ndx, size_t ndx) const noexcept +{ + return get(col_ndx, ndx); +} + +inline void Table::set_olddatetime(size_t col_ndx, size_t ndx, OldDateTime value, bool is_default) +{ + return set(col_ndx, ndx, value, is_default); +} + +inline float Table::get_float(size_t col_ndx, size_t ndx) const noexcept +{ + float f = get(col_ndx, ndx); + return null::is_null_float(f) ? 0.0f : f; +} + +inline void Table::set_float(size_t col_ndx, size_t ndx, float value, bool is_default) +{ + return set(col_ndx, ndx, value, is_default); +} + +inline double Table::get_double(size_t col_ndx, size_t ndx) const noexcept +{ + double d = get(col_ndx, ndx); + return null::is_null_float(d) ? 0.0 : d; +} + +inline void Table::set_double(size_t col_ndx, size_t ndx, double value, bool is_default) +{ + return set(col_ndx, ndx, value, is_default); +} + +inline StringData Table::get_string(size_t col_ndx, size_t ndx) const noexcept +{ + return get(col_ndx, ndx); +} + +inline void Table::set_string(size_t col_ndx, size_t ndx, StringData value, bool is_default) +{ + return set(col_ndx, ndx, value, is_default); +} + +inline size_t Table::set_string_unique(size_t col_ndx, size_t ndx, StringData value) +{ + return set_unique(col_ndx, ndx, value); +} + +inline BinaryData Table::get_binary(size_t col_ndx, size_t ndx) const noexcept +{ + return get(col_ndx, ndx); +} + +inline BinaryIterator Table::get_binary_iterator(size_t col_ndx, size_t ndx) const noexcept +{ + return get(col_ndx, ndx); +} + +inline void Table::set_binary(size_t col_ndx, size_t ndx, BinaryData value, bool is_default) +{ + set(col_ndx, ndx, value, is_default); +} + +inline Mixed Table::get_mixed(size_t col_ndx, size_t ndx) const noexcept +{ + return get(col_ndx, ndx); +} + +inline void Table::set_mixed(size_t col_ndx, size_t ndx, Mixed value, bool is_default) +{ + set(col_ndx, ndx, value, is_default); +} + +inline void Table::set_null(size_t col_ndx, size_t ndx, bool is_default) +{ + set(col_ndx, ndx, null(), is_default); +} + +inline void Table::set_null_unique(size_t col_ndx, size_t ndx) +{ + set_unique(col_ndx, ndx, null()); +} + + // This class groups together information about the target of a link column // This is not a valid link if the target table == nullptr struct LinkTargetInfo { @@ -2080,9 +2427,9 @@ class _impl::TableFriend { return table.release(); } - static Table* create_accessor(ConstSubspecRef shared_spec, Table::Parent* parent_column, size_t parent_row_ndx) + static Table* create_accessor(Spec* shared_spec, Table::Parent* parent_column, size_t parent_row_ndx) { - Allocator& alloc = shared_spec.get_alloc(); + Allocator& alloc = shared_spec->get_alloc(); std::unique_ptr
table(new Table(Table::ref_count_tag(), alloc)); // Throws table->init(shared_spec, parent_column, parent_row_ndx); // Throws return table.release(); @@ -2161,12 +2508,12 @@ class _impl::TableFriend { static Spec& get_spec(Table& table) noexcept { - return table.m_spec; + return *table.m_spec; } static const Spec& get_spec(const Table& table) noexcept { - return table.m_spec; + return *table.m_spec; } static ColumnBase& get_column(const Table& table, size_t col_ndx) @@ -2191,6 +2538,11 @@ class _impl::TableFriend { table.do_swap_rows(row_ndx_1, row_ndx_2); // Throws } + static void do_move_row(Table& table, size_t from_ndx, size_t to_ndx) + { + table.do_move_row(from_ndx, to_ndx); // Throws + } + static void do_merge_rows(Table& table, size_t row_ndx, size_t new_row_ndx) { table.do_merge_rows(row_ndx, new_row_ndx); // Throws @@ -2207,9 +2559,9 @@ class _impl::TableFriend { table.do_set_link(col_ndx, row_ndx, target_row_ndx); // Throws } - static size_t get_num_strong_backlinks(const Table& table, size_t row_ndx) noexcept + static size_t get_backlink_count(const Table& table, size_t row_ndx, bool only_strong_links) noexcept { - return table.get_num_strong_backlinks(row_ndx); + return table.get_backlink_count(row_ndx, only_strong_links); } static void cascade_break_backlinks_to(Table& table, size_t row_ndx, CascadeState& state) @@ -2249,9 +2601,14 @@ class _impl::TableFriend { Table::do_rename_column(desc, column_ndx, name); // Throws } - static void move_column(Descriptor& desc, size_t col_ndx_1, size_t col_ndx_2) + static void add_search_index(Descriptor& desc, size_t column_ndx) { - Table::do_move_column(desc, col_ndx_1, col_ndx_2); // Throws + Table::do_add_search_index(desc, column_ndx); // Throws + } + + static void remove_search_index(Descriptor& desc, size_t column_ndx) + { + Table::do_remove_search_index(desc, column_ndx); // Throws } static void set_link_type(Table& table, size_t column_ndx, LinkType link_type) @@ -2269,7 +2626,7 @@ class _impl::TableFriend { table.batch_erase_rows(row_indexes, is_move_last_over); // Throws } - static Table* get_subtable_accessor(Table& table, size_t col_ndx, size_t row_ndx) noexcept + static TableRef get_subtable_accessor(Table& table, size_t col_ndx, size_t row_ndx) noexcept { return table.get_subtable_accessor(col_ndx, row_ndx); } @@ -2299,6 +2656,11 @@ class _impl::TableFriend { table.adj_acc_swap_rows(row_ndx_1, row_ndx_2); } + static void adj_acc_move_row(Table& table, size_t from_ndx, size_t to_ndx) noexcept + { + table.adj_acc_move_row(from_ndx, to_ndx); + } + static void adj_acc_merge_rows(Table& table, size_t row_ndx_1, size_t row_ndx_2) noexcept { table.adj_acc_merge_rows(row_ndx_1, row_ndx_2); @@ -2335,11 +2697,6 @@ class _impl::TableFriend { table.adj_erase_column(col_ndx); } - static void adj_move_column(Table& table, size_t col_ndx_1, size_t col_ndx_2) noexcept - { - table.adj_move_column(col_ndx_1, col_ndx_2); - } - static bool is_marked(const Table& table) noexcept { return table.is_marked(); @@ -2387,14 +2744,14 @@ class _impl::TableFriend { table.refresh_accessor_tree(); // Throws } - static void set_ndx_in_parent(Table& table, size_t ndx_in_parent) noexcept + static void refresh_spec_accessor(Table& table) { - table.set_ndx_in_parent(ndx_in_parent); + table.refresh_spec_accessor(); // Throws } - static void set_shared_subspec_ndx_in_parent(Table& table, size_t spec_ndx_in_parent) noexcept + static void set_ndx_in_parent(Table& table, size_t ndx_in_parent) noexcept { - table.m_spec.set_ndx_in_parent(spec_ndx_in_parent); + table.set_ndx_in_parent(ndx_in_parent); } static bool is_link_type(ColumnType type) noexcept diff --git a/Pods/Realm/include/core/realm/table_accessors.hpp b/Pods/Realm/include/core/realm/table_accessors.hpp deleted file mode 100644 index d08edee..0000000 --- a/Pods/Realm/include/core/realm/table_accessors.hpp +++ /dev/null @@ -1,1981 +0,0 @@ -/************************************************************************* - * - * Copyright 2016 Realm Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - **************************************************************************/ - -#ifndef REALM_TABLE_ACCESSORS_HPP -#define REALM_TABLE_ACCESSORS_HPP - -#include -#include - -#include -#include - -#include - -namespace realm { - - -/// A convenience base class for Spec classes that are to be used with -/// BasicTable. -/// -/// There are two reasons why you might want to derive your spec class -/// from this one. First, it offers short hand names for each of the -/// available column types. Second, it makes it easier when you do not -/// want to specify colum names or convenience methods, since suitable -/// fallbacks are defined here. -struct SpecBase { - typedef int64_t Int; - typedef bool Bool; - typedef realm::OldDateTime OldDateTime; - typedef float Float; - typedef double Double; - typedef realm::StringData String; - typedef realm::BinaryData Binary; - typedef realm::Mixed Mixed; - - template - class Enum { - public: - typedef E enum_type; - Enum(E v) - : m_value(v) - { - } - operator E() const - { - return m_value; - } - - private: - E m_value; - }; - - template - class Subtable { - public: - typedef T table_type; - Subtable(T* t) - : m_table(t) - { - } - operator T*() const - { - return m_table; - } - - private: - T* m_table; - }; - - /// By default, there are no static column names defined for a - /// BasicTable. One may define a set of column mames as follows: - /// - /// \code{.cpp} - /// - /// struct MyTableSpec: SpecBase { - /// typedef TypeAppend::type Columns1; - /// typedef TypeAppend::type Columns; - /// - /// template