Merge branch 'feature/swift4' into develop

This commit is contained in:
Riley Testut 2017-10-12 15:36:46 -07:00
commit a8e5e3ab09
88 changed files with 1410 additions and 847 deletions

@ -1 +1 @@
Subproject commit 6e0ec5aedac746d1a5ea05d450884f1a9ad7b509 Subproject commit 4854926b2a21fe0c6e652dc1ccb4e7abdef3dcb6

@ -1 +1 @@
Subproject commit 56401b9649f8abe971e3a66be1a3bb2cd984a1a0 Subproject commit 7babfcf93a32916ad06989c9a63681acf7f07d50

@ -1 +1 @@
Subproject commit 4e678e7eac511d342aab86522eede9fdb7b29108 Subproject commit 9d714254ace7bd5d63805d434f4ccd1f2a947951

@ -1 +1 @@
Subproject commit 394627784bd0643c26e0fc95feafa5c960786a7c Subproject commit a354c3700fc4576bbefb6a8324bcd7a121d9b934

View File

@ -709,7 +709,7 @@
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastSwiftUpdateCheck = 0700; LastSwiftUpdateCheck = 0700;
LastUpgradeCheck = 0820; LastUpgradeCheck = 0910;
ORGANIZATIONNAME = "Riley Testut"; ORGANIZATIONNAME = "Riley Testut";
TargetAttributes = { TargetAttributes = {
BF14D8941DE7A512002CA1BE = { BF14D8941DE7A512002CA1BE = {
@ -720,7 +720,7 @@
BFFA71D61AAC406100EE9DD1 = { BFFA71D61AAC406100EE9DD1 = {
CreatedOnToolsVersion = 6.3; CreatedOnToolsVersion = 6.3;
DevelopmentTeam = 6XVY5G3U44; DevelopmentTeam = 6XVY5G3U44;
LastSwiftMigration = 0800; LastSwiftMigration = 0900;
ProvisioningStyle = Automatic; ProvisioningStyle = Automatic;
SystemCapabilities = { SystemCapabilities = {
com.apple.iCloud = { com.apple.iCloud = {
@ -996,14 +996,20 @@
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 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_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
@ -1046,14 +1052,20 @@
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 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_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
@ -1098,7 +1110,8 @@
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Delta/Supporting Files/Delta-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Delta/Supporting Files/Delta-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0; SWIFT_SWIFT3_OBJC_INFERENCE = Off;
SWIFT_VERSION = 4.0;
}; };
name = Debug; name = Debug;
}; };
@ -1119,7 +1132,8 @@
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Delta/Supporting Files/Delta-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Delta/Supporting Files/Delta-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 3.0; SWIFT_SWIFT3_OBJC_INFERENCE = Off;
SWIFT_VERSION = 4.0;
}; };
name = Release; name = Release;
}; };

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "0830" LastUpgradeVersion = "0910"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "NO" parallelizeBuildables = "NO"
@ -96,6 +96,7 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<Testables> <Testables>
</Testables> </Testables>
@ -115,6 +116,7 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0" launchStyle = "0"
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"

View File

@ -79,7 +79,7 @@ extension AppDelegate
{ {
self.window?.tintColor = UIColor.deltaPurple self.window?.tintColor = UIColor.deltaPurple
UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).defaultTextAttributes[NSForegroundColorAttributeName] = UIColor.white UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).defaultTextAttributes[NSAttributedStringKey.foregroundColor.rawValue] = UIColor.white
} }
} }
@ -90,7 +90,7 @@ extension AppDelegate
return self.openURL(url) return self.openURL(url)
} }
@discardableResult fileprivate func openURL(_ url: URL) -> Bool @discardableResult private func openURL(_ url: URL) -> Bool
{ {
guard url.isFileURL else { return false } guard url.isFileURL else { return false }

View File

@ -45,15 +45,15 @@ class GridCollectionViewCell: UICollectionViewCell
} }
} }
fileprivate var vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: UIBlurEffect(style: .dark))) private var vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: UIBlurEffect(style: .dark)))
fileprivate var imageViewWidthConstraint: NSLayoutConstraint! private var imageViewWidthConstraint: NSLayoutConstraint!
fileprivate var imageViewHeightConstraint: NSLayoutConstraint! private var imageViewHeightConstraint: NSLayoutConstraint!
fileprivate var textLabelBottomAnchorConstraint: NSLayoutConstraint! private var textLabelBottomAnchorConstraint: NSLayoutConstraint!
fileprivate var textLabelVerticalSpacingConstraint: NSLayoutConstraint! private var textLabelVerticalSpacingConstraint: NSLayoutConstraint!
fileprivate var textLabelFocusedVerticalSpacingConstraint: NSLayoutConstraint? private var textLabelFocusedVerticalSpacingConstraint: NSLayoutConstraint?
override init(frame: CGRect) override init(frame: CGRect)
{ {

View File

@ -17,7 +17,7 @@ class ListMenuViewController: UITableViewController
set { self.dataSource.items = newValue } set { self.dataSource.items = newValue }
} }
fileprivate let dataSource = RSTArrayTableViewDataSource<MenuItem>(items: []) private let dataSource = RSTArrayTableViewDataSource<MenuItem>(items: [])
override var preferredContentSize: CGSize { override var preferredContentSize: CGSize {
get { get {

View File

@ -10,7 +10,7 @@ import UIKit
extension UINavigationBar extension UINavigationBar
{ {
fileprivate var defaultTitleTextAttributes: [String: Any]? { fileprivate var defaultTitleTextAttributes: [NSAttributedStringKey: Any]? {
if let textAttributes = self._defaultTitleTextAttributes if let textAttributes = self._defaultTitleTextAttributes
{ {
return textAttributes return textAttributes
@ -35,7 +35,7 @@ extension UINavigationBar
return textAttributes return textAttributes
} }
fileprivate var _defaultTitleTextAttributes: [String: Any]? { private var _defaultTitleTextAttributes: [NSAttributedStringKey: Any]? {
guard self.titleTextAttributes == nil else { return self.titleTextAttributes } guard self.titleTextAttributes == nil else { return self.titleTextAttributes }
guard guard
@ -59,11 +59,11 @@ class PopoverMenuButton: UIControl
} }
} }
fileprivate let textLabel: UILabel private let textLabel: UILabel
fileprivate let arrowLabel: UILabel private let arrowLabel: UILabel
fileprivate let stackView: UIStackView private let stackView: UIStackView
fileprivate var parentNavigationBar: UINavigationBar? { private var parentNavigationBar: UINavigationBar? {
guard let navigationController = self.parentViewController as? UINavigationController ?? self.parentViewController?.navigationController else { return nil } guard let navigationController = self.parentViewController as? UINavigationController ?? self.parentViewController?.navigationController else { return nil }
guard self.isDescendant(of: navigationController.navigationBar) else { return nil } guard self.isDescendant(of: navigationController.navigationBar) else { return nil }

View File

@ -60,7 +60,7 @@ final class DatabaseManager: NSPersistentContainer
{ {
static let shared = DatabaseManager() static let shared = DatabaseManager()
fileprivate var gamesDatabase: GamesDatabase? = nil private var gamesDatabase: GamesDatabase? = nil
private init() private init()
{ {
@ -97,7 +97,7 @@ extension DatabaseManager
//MARK: - Preparation - //MARK: - Preparation -
private extension DatabaseManager private extension DatabaseManager
{ {
func prepareDatabase(completion: @escaping (Void) -> Void) func prepareDatabase(completion: @escaping () -> Void)
{ {
self.performBackgroundTask { (context) in self.performBackgroundTask { (context) in

View File

@ -15,9 +15,9 @@ class InputStreamOutputWriter: NSObject
let inputStream: InputStream let inputStream: InputStream
let outputStream: OutputStream let outputStream: OutputStream
fileprivate var completion: ((Error?) -> Void)? private var completion: ((Error?) -> Void)?
fileprivate var dataBuffer = Data(capacity: MaximumBufferLength * 2) private var dataBuffer = Data(capacity: MaximumBufferLength * 2)
init(inputStream: InputStream, outputStream: OutputStream) init(inputStream: InputStream, outputStream: OutputStream)
{ {

View File

@ -61,7 +61,7 @@ public class ControllerSkin: _ControllerSkin
return self.controllerSkin?.isDebugModeEnabled ?? false return self.controllerSkin?.isDebugModeEnabled ?? false
} }
fileprivate lazy var controllerSkin: DeltaCore.ControllerSkin? = { private lazy var controllerSkin: DeltaCore.ControllerSkin? = {
let controllerSkin = self.isStandard ? DeltaCore.ControllerSkin.standardControllerSkin(for: self.gameType) : DeltaCore.ControllerSkin(fileURL: self.fileURL) let controllerSkin = self.isStandard ? DeltaCore.ControllerSkin.standardControllerSkin(for: self.gameType) : DeltaCore.ControllerSkin(fileURL: self.fileURL)
return controllerSkin return controllerSkin
}() }()

View File

@ -13,7 +13,7 @@ import DeltaCore
@objc(GameControllerInputMapping) @objc(GameControllerInputMapping)
public class GameControllerInputMapping: _GameControllerInputMapping public class GameControllerInputMapping: _GameControllerInputMapping
{ {
fileprivate var inputMapping: DeltaCore.GameControllerInputMapping { private var inputMapping: DeltaCore.GameControllerInputMapping {
get { return self.deltaCoreInputMapping as! DeltaCore.GameControllerInputMapping } get { return self.deltaCoreInputMapping as! DeltaCore.GameControllerInputMapping }
set { self.deltaCoreInputMapping = newValue } set { self.deltaCoreInputMapping = newValue }
} }

View File

@ -68,7 +68,7 @@ class GamesDatabase
{ {
static let version = -1 static let version = -1
fileprivate let connection: Connection private let connection: Connection
init() throws init() throws
{ {

View File

@ -15,9 +15,9 @@ class GamesDatabaseBrowserViewController: UITableViewController
{ {
var selectionHandler: ((GameMetadata) -> Void)? var selectionHandler: ((GameMetadata) -> Void)?
fileprivate let database: GamesDatabase? private let database: GamesDatabase?
fileprivate let dataSource: RSTArrayTableViewPrefetchingDataSource<GameMetadata, UIImage> private let dataSource: RSTArrayTableViewPrefetchingDataSource<GameMetadata, UIImage>
override init(style: UITableViewStyle) { override init(style: UITableViewStyle) {
fatalError() fatalError()

View File

@ -72,11 +72,11 @@ class GameViewController: DeltaCore.GameViewController
} }
//MARK: - Private Properties - //MARK: - Private Properties -
fileprivate var pauseViewController: PauseViewController? private var pauseViewController: PauseViewController?
fileprivate var pausingGameController: GameController? private var pausingGameController: GameController?
// Prevents the same save state from being saved multiple times // Prevents the same save state from being saved multiple times
fileprivate var pausedSaveState: PausedSaveState? { private var pausedSaveState: PausedSaveState? {
didSet didSet
{ {
if let saveState = oldValue, self.pausedSaveState == nil if let saveState = oldValue, self.pausedSaveState == nil
@ -93,17 +93,17 @@ class GameViewController: DeltaCore.GameViewController
} }
} }
fileprivate var _isLoadingSaveState = false private var _isLoadingSaveState = false
fileprivate var context = CIContext(options: [kCIContextWorkingColorSpace: NSNull()]) private var context = CIContext(options: [kCIContextWorkingColorSpace: NSNull()])
// Sustain Buttons // Sustain Buttons
fileprivate var isSelectingSustainedButtons = false private var isSelectingSustainedButtons = false
fileprivate var sustainInputsMapping: SustainInputsMapping? private var sustainInputsMapping: SustainInputsMapping?
fileprivate var sustainButtonsContentView: UIView! private var sustainButtonsContentView: UIView!
fileprivate var sustainButtonsBlurView: UIVisualEffectView! private var sustainButtonsBlurView: UIVisualEffectView!
fileprivate var sustainButtonsBackgroundView: RSTPlaceholderView! private var sustainButtonsBackgroundView: RSTPlaceholderView!
required init() required init()
{ {
@ -460,7 +460,7 @@ private extension GameViewController
/// Save States /// Save States
extension GameViewController: SaveStatesViewControllerDelegate extension GameViewController: SaveStatesViewControllerDelegate
{ {
fileprivate func updateAutoSaveState() private func updateAutoSaveState()
{ {
// Ensures game is non-nil and also a Game subclass // Ensures game is non-nil and also a Game subclass
guard let game = self.game as? Game else { return } guard let game = self.game as? Game else { return }
@ -513,7 +513,7 @@ extension GameViewController: SaveStatesViewControllerDelegate
} }
} }
fileprivate func update(_ saveState: SaveState, with replacementSaveState: SaveStateProtocol? = nil) private func update(_ saveState: SaveState, with replacementSaveState: SaveStateProtocol? = nil)
{ {
let isRunning = (self.emulatorCore?.state == .running) let isRunning = (self.emulatorCore?.state == .running)
@ -567,7 +567,7 @@ extension GameViewController: SaveStatesViewControllerDelegate
} }
} }
fileprivate func load(_ saveState: SaveStateProtocol) private func load(_ saveState: SaveStateProtocol)
{ {
let isRunning = (self.emulatorCore?.state == .running) let isRunning = (self.emulatorCore?.state == .running)

View File

@ -27,7 +27,7 @@ class PreviewGameViewController: DeltaCore.GameViewController
} }
} }
fileprivate var emulatorCoreQueue = DispatchQueue(label: "com.rileytestut.Delta.PreviewGameViewController.emulatorCoreQueue", qos: .userInitiated) private var emulatorCoreQueue = DispatchQueue(label: "com.rileytestut.Delta.PreviewGameViewController.emulatorCoreQueue", qos: .userInitiated)
override var game: GameProtocol? { override var game: GameProtocol? {
willSet { willSet {

View File

@ -18,7 +18,7 @@ internal extension UILabel
context.minimumScaleFactor = self.minimumScaleFactor context.minimumScaleFactor = self.minimumScaleFactor
// Using self.attributedString returns incorrect calculations, so we create our own attributed string // Using self.attributedString returns incorrect calculations, so we create our own attributed string
let attributedString = NSAttributedString(string: text, attributes: [NSFontAttributeName: self.font]) let attributedString = NSAttributedString(string: text, attributes: [.font: self.font])
attributedString.boundingRect(with: self.bounds.size, options: [.usesLineFragmentOrigin, .usesFontLeading], context: context) attributedString.boundingRect(with: self.bounds.size, options: [.usesLineFragmentOrigin, .usesFontLeading], context: context)
let scaleFactor = context.actualScaleFactor let scaleFactor = context.actualScaleFactor

View File

@ -43,16 +43,16 @@ class GameCollectionViewController: UICollectionViewController
weak var activeEmulatorCore: EmulatorCore? weak var activeEmulatorCore: EmulatorCore?
fileprivate var activeSaveState: SaveStateProtocol? private var activeSaveState: SaveStateProtocol?
fileprivate let dataSource: RSTFetchedResultsCollectionViewPrefetchingDataSource<Game, UIImage> private let dataSource: RSTFetchedResultsCollectionViewPrefetchingDataSource<Game, UIImage>
fileprivate let prototypeCell = GridCollectionViewCell() private let prototypeCell = GridCollectionViewCell()
fileprivate var _performing3DTouchTransition = false private var _performing3DTouchTransition = false
fileprivate weak var _destination3DTouchTransitionViewController: UIViewController? private weak var _destination3DTouchTransitionViewController: UIViewController?
fileprivate var _renameAction: UIAlertAction? private var _renameAction: UIAlertAction?
fileprivate var _changingArtworkGame: Game? private var _changingArtworkGame: Game?
required init?(coder aDecoder: NSCoder) required init?(coder aDecoder: NSCoder)
{ {
@ -340,7 +340,7 @@ private extension GameCollectionViewController
func rename(_ game: Game, with name: String) func rename(_ game: Game, with name: String)
{ {
guard name.characters.count > 0 else { return } guard name.count > 0 else { return }
DatabaseManager.shared.performBackgroundTask { (context) in DatabaseManager.shared.performBackgroundTask { (context) in
let game = context.object(with: game.objectID) as! Game let game = context.object(with: game.objectID) as! Game
@ -401,7 +401,7 @@ private extension GameCollectionViewController
@objc func textFieldTextDidChange(_ textField: UITextField) @objc func textFieldTextDidChange(_ textField: UITextField)
{ {
let text = textField.text ?? "" let text = textField.text ?? ""
self._renameAction?.isEnabled = text.characters.count > 0 self._renameAction?.isEnabled = text.count > 0
} }
@objc func handleLongPressGesture(_ gestureRecognizer: UILongPressGestureRecognizer) @objc func handleLongPressGesture(_ gestureRecognizer: UILongPressGestureRecognizer)

View File

@ -35,11 +35,11 @@ class GamesViewController: UIViewController
} }
} }
fileprivate var pageViewController: UIPageViewController! private var pageViewController: UIPageViewController!
fileprivate var placeholderView: RSTPlaceholderView! private var placeholderView: RSTPlaceholderView!
fileprivate var pageControl: UIPageControl! private var pageControl: UIPageControl!
fileprivate let fetchedResultsController: NSFetchedResultsController<NSFetchRequestResult> private let fetchedResultsController: NSFetchedResultsController<NSFetchRequestResult>
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
fatalError("initWithNibName: not implemented") fatalError("initWithNibName: not implemented")
@ -273,7 +273,7 @@ private extension GamesViewController
/// Importing /// Importing
extension GamesViewController: ImportControllerDelegate extension GamesViewController: ImportControllerDelegate
{ {
@IBAction fileprivate func importFiles() @IBAction private func importFiles()
{ {
var documentTypes = Set(System.supportedSystems.map { $0.gameType.rawValue }) var documentTypes = Set(System.supportedSystems.map { $0.gameType.rawValue })
documentTypes.insert(kUTTypeZipArchive as String) documentTypes.insert(kUTTypeZipArchive as String)

View File

@ -10,9 +10,9 @@ import UIKit
class GamesStoryboardSegue: UIStoryboardSegue class GamesStoryboardSegue: UIStoryboardSegue
{ {
fileprivate let animator: UIViewPropertyAnimator private let animator: UIViewPropertyAnimator
fileprivate var isPresenting: Bool = true private var isPresenting: Bool = true
override init(identifier: String?, source: UIViewController, destination: UIViewController) override init(identifier: String?, source: UIViewController, destination: UIViewController)
{ {

View File

@ -10,9 +10,9 @@ import UIKit
class InitialGamesStoryboardSegue: UIStoryboardSegue class InitialGamesStoryboardSegue: UIStoryboardSegue
{ {
fileprivate let animator: UIViewPropertyAnimator private let animator: UIViewPropertyAnimator
fileprivate var isPresenting: Bool = true private var isPresenting: Bool = true
override init(identifier: String?, source: UIViewController, destination: UIViewController) override init(identifier: String?, source: UIViewController, destination: UIViewController)
{ {

View File

@ -15,7 +15,7 @@ class PhotoLibraryImportOption: NSObject, ImportOption
let image: UIImage? = nil let image: UIImage? = nil
private let presentingViewController: UIViewController private let presentingViewController: UIViewController
fileprivate var completionHandler: ((Set<URL>?) -> Void)? private var completionHandler: ((Set<URL>?) -> Void)?
init(presentingViewController: UIViewController) init(presentingViewController: UIViewController)
{ {

View File

@ -37,14 +37,14 @@ class ImportController: NSObject
var delegate: ImportControllerDelegate? var delegate: ImportControllerDelegate?
var importOptions: [ImportOption]? var importOptions: [ImportOption]?
fileprivate weak var presentingViewController: UIViewController? private weak var presentingViewController: UIViewController?
// Store presentedViewController separately, since when we dismiss we don't know if it has already been dismissed. // Store presentedViewController separately, since when we dismiss we don't know if it has already been dismissed.
// Calling dismiss on presentingViewController in that case would dismiss presentingViewController, which is bad. // Calling dismiss on presentingViewController in that case would dismiss presentingViewController, which is bad.
fileprivate weak var presentedViewController: UIViewController? private weak var presentedViewController: UIViewController?
fileprivate let importQueue: OperationQueue private let importQueue: OperationQueue
fileprivate let fileCoordinator: NSFileCoordinator private let fileCoordinator: NSFileCoordinator
init(documentTypes: Set<String>) init(documentTypes: Set<String>)
{ {
@ -61,7 +61,7 @@ class ImportController: NSObject
super.init() super.init()
} }
fileprivate func presentImportController(from presentingViewController: UIViewController, animated: Bool, completionHandler: ((Void) -> Void)?) fileprivate func presentImportController(from presentingViewController: UIViewController, animated: Bool, completionHandler: (() -> Void)?)
{ {
self.presentingViewController = presentingViewController self.presentingViewController = presentingViewController
@ -104,12 +104,12 @@ class ImportController: NSObject
#endif #endif
} }
@objc fileprivate func cancel() @objc private func cancel()
{ {
self.finish(with: nil, errors: []) self.finish(with: nil, errors: [])
} }
fileprivate func finish(with urls: Set<URL>?, errors: [Error]) private func finish(with urls: Set<URL>?, errors: [Error])
{ {
if let urls = urls if let urls = urls
{ {
@ -241,7 +241,7 @@ extension UIViewController
} }
} }
func present(_ importController: ImportController, animated: Bool, completion: ((Void) -> Void)?) func present(_ importController: ImportController, animated: Bool, completion: (() -> Void)?)
{ {
self.importController = importController self.importController = importController

View File

@ -10,10 +10,10 @@ import UIKit
class LaunchViewController: UIViewController class LaunchViewController: UIViewController
{ {
@IBOutlet fileprivate var gameViewContainerView: UIView! @IBOutlet private var gameViewContainerView: UIView!
fileprivate var gameViewController: GameViewController! private var gameViewController: GameViewController!
fileprivate var presentedGameViewController: Bool = false private var presentedGameViewController: Bool = false
override var preferredStatusBarStyle: UIStatusBarStyle { override var preferredStatusBarStyle: UIStatusBarStyle {
return self.gameViewController?.preferredStatusBarStyle ?? .lightContent return self.gameViewController?.preferredStatusBarStyle ?? .lightContent

View File

@ -13,7 +13,10 @@ import DeltaCore
import Roxas import Roxas
private let CheatPrefixAttribute = "prefix" private extension NSAttributedStringKey
{
static let cheatPrefix = NSAttributedStringKey("CheatPrefix")
}
class CheatTextView: UITextView class CheatTextView: UITextView
{ {
@ -23,7 +26,7 @@ class CheatTextView: UITextView
} }
} }
@NSCopying fileprivate var attributedFormat: NSAttributedString? @NSCopying private var attributedFormat: NSAttributedString?
required init?(coder aDecoder: NSCoder) required init?(coder aDecoder: NSCoder)
{ {
@ -44,9 +47,9 @@ extension CheatTextView
if let format = self.cheatFormat, let font = self.font if let format = self.cheatFormat, let font = self.font
{ {
let characterWidth = ("A" as NSString).size(attributes: [NSFontAttributeName: font]).width let characterWidth = ("A" as NSString).size(withAttributes: [.font: font]).width
let width = characterWidth * CGFloat(format.format.characters.count) let width = characterWidth * CGFloat(format.format.count)
self.textContainer.size = CGSize(width: width, height: 0) self.textContainer.size = CGSize(width: width, height: 0)
} }
} }
@ -79,7 +82,7 @@ private extension CheatTextView
if let prefixString = prefixString, prefixString.length > 0 if let prefixString = prefixString, prefixString.length > 0
{ {
attributedString.addAttribute(CheatPrefixAttribute, value: prefixString, range: NSRange(location: 0, length: 1)) attributedString.addAttribute(.cheatPrefix, value: prefixString, range: NSRange(location: 0, length: 1))
} }
attributedFormat.append(attributedString) attributedFormat.append(attributedString)
@ -105,7 +108,7 @@ private extension CheatTextView
extension CheatTextView: NSLayoutManagerDelegate extension CheatTextView: NSLayoutManagerDelegate
{ {
func layoutManager(_ layoutManager: NSLayoutManager, shouldGenerateGlyphs glyphs: UnsafePointer<CGGlyph>, properties props: UnsafePointer<NSGlyphProperty>, characterIndexes charIndexes: UnsafePointer<Int>, font aFont: UIFont, forGlyphRange glyphRange: NSRange) -> Int func layoutManager(_ layoutManager: NSLayoutManager, shouldGenerateGlyphs glyphs: UnsafePointer<CGGlyph>, properties props: UnsafePointer<NSLayoutManager.GlyphProperty>, characterIndexes charIndexes: UnsafePointer<Int>, font aFont: UIFont, forGlyphRange glyphRange: NSRange) -> Int
{ {
// Returning 0 = let the layoutManager do the normal logic // Returning 0 = let the layoutManager do the normal logic
guard let attributedFormat = self.attributedFormat else { return 0 } guard let attributedFormat = self.attributedFormat else { return 0 }
@ -118,7 +121,7 @@ extension CheatTextView: NSLayoutManagerDelegate
// Allocate our replacement buffers // Allocate our replacement buffers
let glyphBuffer = UnsafeMutablePointer<CGGlyph>.allocate(capacity: bufferSize) let glyphBuffer = UnsafeMutablePointer<CGGlyph>.allocate(capacity: bufferSize)
let propertyBuffer = UnsafeMutablePointer<NSGlyphProperty>.allocate(capacity: bufferSize) let propertyBuffer = UnsafeMutablePointer<NSLayoutManager.GlyphProperty>.allocate(capacity: bufferSize)
let characterBuffer = UnsafeMutablePointer<Int>.allocate(capacity: bufferSize) let characterBuffer = UnsafeMutablePointer<Int>.allocate(capacity: bufferSize)
var offset = 0 var offset = 0
@ -128,10 +131,10 @@ extension CheatTextView: NSLayoutManagerDelegate
// The index the actual character maps to in the cheat format // The index the actual character maps to in the cheat format
let characterIndex = charIndexes[i] % attributedFormat.length let characterIndex = charIndexes[i] % attributedFormat.length
if let prefix = attributedFormat.attributes(at: characterIndex, effectiveRange: nil)[CheatPrefixAttribute] as? String if let prefix = attributedFormat.attributes(at: characterIndex, effectiveRange: nil)[.cheatPrefix] as? String
{ {
// If there is a prefix string, we insert the glyphs (and associated properties/character indexes) first // If there is a prefix string, we insert the glyphs (and associated properties/character indexes) first
let prefixCount = prefix.characters.count let prefixCount = prefix.count
for j in 0 ..< prefixCount for j in 0 ..< prefixCount
{ {

View File

@ -51,7 +51,7 @@ struct CheatValidator
// Remove newline characters (code should already be formatted) // Remove newline characters (code should already be formatted)
let sanitizedCode = (cheat.code as NSString).replacingOccurrences(of: "\n", with: "") let sanitizedCode = (cheat.code as NSString).replacingOccurrences(of: "\n", with: "")
if sanitizedCode.characters.count % self.format.format.characters.count != 0 if sanitizedCode.count % self.format.format.count != 0
{ {
throw Error.invalidCode throw Error.invalidCode
} }

View File

@ -29,7 +29,7 @@ class CheatsViewController: UITableViewController
weak var delegate: CheatsViewControllerDelegate? weak var delegate: CheatsViewControllerDelegate?
fileprivate let dataSource = RSTFetchedResultsTableViewDataSource<Cheat>(fetchedResultsController: NSFetchedResultsController()) private let dataSource = RSTFetchedResultsTableViewDataSource<Cheat>(fetchedResultsController: NSFetchedResultsController())
} }
extension CheatsViewController extension CheatsViewController

View File

@ -43,19 +43,19 @@ class EditCheatViewController: UITableViewController
var isPreviewing = false var isPreviewing = false
fileprivate var supportedCheatFormats: [CheatFormat]! private var supportedCheatFormats: [CheatFormat]!
fileprivate var selectedCheatFormat: CheatFormat { private var selectedCheatFormat: CheatFormat {
let cheatFormat = self.supportedCheatFormats[self.typeSegmentedControl.selectedSegmentIndex] let cheatFormat = self.supportedCheatFormats[self.typeSegmentedControl.selectedSegmentIndex]
return cheatFormat return cheatFormat
} }
fileprivate var mutableCheat: Cheat! private var mutableCheat: Cheat!
fileprivate var managedObjectContext = DatabaseManager.shared.newBackgroundContext() private var managedObjectContext = DatabaseManager.shared.newBackgroundContext()
@IBOutlet fileprivate var nameTextField: UITextField! @IBOutlet private var nameTextField: UITextField!
@IBOutlet fileprivate var typeSegmentedControl: UISegmentedControl! @IBOutlet private var typeSegmentedControl: UISegmentedControl!
@IBOutlet fileprivate var codeTextView: CheatTextView! @IBOutlet private var codeTextView: CheatTextView!
override var previewActionItems: [UIPreviewActionItem] override var previewActionItems: [UIPreviewActionItem]
{ {
@ -132,7 +132,7 @@ extension EditCheatViewController
// Update UI // Update UI
if name.characters.count == 0 if name.count == 0
{ {
self.title = NSLocalizedString("Cheat", comment: "") self.title = NSLocalizedString("Cheat", comment: "")
} }
@ -224,7 +224,7 @@ private extension EditCheatViewController
@IBAction func updateCheatName(_ sender: UITextField) @IBAction func updateCheatName(_ sender: UITextField)
{ {
var title = sender.text ?? "" var title = sender.text ?? ""
if title.characters.count == 0 if title.count == 0
{ {
title = NSLocalizedString("Cheat", comment: "") title = NSLocalizedString("Cheat", comment: "")
} }
@ -325,7 +325,7 @@ private extension EditCheatViewController
sender.resignFirstResponder() sender.resignFirstResponder()
} }
func presentErrorAlert(title: String, message: String, handler: ((Void) -> Void)?) func presentErrorAlert(title: String, message: String, handler: (() -> Void)?)
{ {
DispatchQueue.main.async { DispatchQueue.main.async {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
@ -382,7 +382,9 @@ extension EditCheatViewController: UITextViewDelegate
// We need to manually add back the attributes when manually modifying the underlying text storage // We need to manually add back the attributes when manually modifying the underlying text storage
// Otherwise, pasting text into an empty text view will result in the wrong font being used // Otherwise, pasting text into an empty text view will result in the wrong font being used
let attributedString = NSAttributedString(string: sanitizedText, attributes: textView.typingAttributes) let attributes = Dictionary(uniqueKeysWithValues: textView.typingAttributes.map { (key, value) in (NSAttributedStringKey(key), value) })
let attributedString = NSAttributedString(string: sanitizedText, attributes: attributes)
textView.textStorage.replaceCharacters(in: range, with: attributedString) textView.textStorage.replaceCharacters(in: range, with: attributedString)
// We must add attributedString.length, not range.length, in case the attributed string's length differs // We must add attributedString.length, not range.length, in case the attributed string's length differs

View File

@ -23,12 +23,12 @@ class GridMenuViewController: UICollectionViewController
get { return self.collectionView?.contentSize ?? CGSize.zero } get { return self.collectionView?.contentSize ?? CGSize.zero }
} }
fileprivate let dataSource = RSTArrayCollectionViewDataSource<MenuItem>(items: []) private let dataSource = RSTArrayCollectionViewDataSource<MenuItem>(items: [])
fileprivate var prototypeCell = GridCollectionViewCell() private var prototypeCell = GridCollectionViewCell()
fileprivate var previousIndexPath: IndexPath? = nil private var previousIndexPath: IndexPath? = nil
fileprivate var registeredKVOObservers = Set<NSKeyValueObservation>() private var registeredKVOObservers = Set<NSKeyValueObservation>()
init() init()
{ {

View File

@ -38,9 +38,9 @@ class PauseViewController: UIViewController, PauseInfoProviding
/// Save States /// Save States
weak var saveStatesViewControllerDelegate: SaveStatesViewControllerDelegate? weak var saveStatesViewControllerDelegate: SaveStatesViewControllerDelegate?
fileprivate var saveStatesViewControllerMode = SaveStatesViewController.Mode.loading private var saveStatesViewControllerMode = SaveStatesViewController.Mode.loading
fileprivate var pauseNavigationController: UINavigationController! private var pauseNavigationController: UINavigationController!
/// UIViewController /// UIViewController
override var preferredContentSize: CGSize { override var preferredContentSize: CGSize {

View File

@ -12,7 +12,7 @@ class SaveStatesCollectionHeaderView: UICollectionReusableView
{ {
let textLabel = UILabel() let textLabel = UILabel()
fileprivate let vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: UIBlurEffect(style: .dark))) private let vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: UIBlurEffect(style: .dark)))
var isTextLabelVibrancyEnabled = true { var isTextLabelVibrancyEnabled = true {
didSet { didSet {

View File

@ -59,18 +59,18 @@ class SaveStatesViewController: UICollectionViewController
} }
} }
fileprivate var vibrancyView: UIVisualEffectView! private var vibrancyView: UIVisualEffectView!
fileprivate var placeholderView: RSTPlaceholderView! private var placeholderView: RSTPlaceholderView!
fileprivate var prototypeCell = GridCollectionViewCell() private var prototypeCell = GridCollectionViewCell()
fileprivate var prototypeCellWidthConstraint: NSLayoutConstraint! private var prototypeCellWidthConstraint: NSLayoutConstraint!
fileprivate var prototypeHeader = SaveStatesCollectionHeaderView() private var prototypeHeader = SaveStatesCollectionHeaderView()
fileprivate let dataSource: RSTFetchedResultsCollectionViewPrefetchingDataSource<SaveState, UIImage> private let dataSource: RSTFetchedResultsCollectionViewPrefetchingDataSource<SaveState, UIImage>
fileprivate var emulatorCoreSaveState: SaveStateProtocol? private var emulatorCoreSaveState: SaveStateProtocol?
fileprivate let dateFormatter: DateFormatter private let dateFormatter: DateFormatter
required init?(coder aDecoder: NSCoder) required init?(coder aDecoder: NSCoder)
{ {
@ -377,7 +377,7 @@ private extension SaveStatesViewController
func rename(_ saveState: SaveState, with name: String?) func rename(_ saveState: SaveState, with name: String?)
{ {
var name = name var name = name
if (name ?? "").characters.count == 0 if (name ?? "").count == 0
{ {
// When text is nil, we know to show the timestamp instead // When text is nil, we know to show the timestamp instead
name = nil name = nil
@ -562,7 +562,7 @@ private extension SaveStatesViewController
//MARK: - 3D Touch - //MARK: - 3D Touch -
extension SaveStatesViewController: UIViewControllerPreviewingDelegate extension SaveStatesViewController: UIViewControllerPreviewingDelegate
{ {
fileprivate func prepareEmulatorCoreSaveState() private func prepareEmulatorCoreSaveState()
{ {
guard let emulatorCore = self.emulatorCore else { return } guard let emulatorCore = self.emulatorCore else { return }

View File

@ -10,8 +10,8 @@ import UIKit
class PauseStoryboardSegue: UIStoryboardSegue class PauseStoryboardSegue: UIStoryboardSegue
{ {
fileprivate let animator: UIViewPropertyAnimator private let animator: UIViewPropertyAnimator
fileprivate let presentationController: PausePresentationController private let presentationController: PausePresentationController
override init(identifier: String?, source: UIViewController, destination: UIViewController) override init(identifier: String?, source: UIViewController, destination: UIViewController)
{ {

View File

@ -26,7 +26,7 @@ class ControllerSkinsViewController: UITableViewController
} }
} }
fileprivate let dataSource: RSTFetchedResultsTableViewPrefetchingDataSource<ControllerSkin, UIImage> private let dataSource: RSTFetchedResultsTableViewPrefetchingDataSource<ControllerSkin, UIImage>
required init?(coder aDecoder: NSCoder) required init?(coder aDecoder: NSCoder)
{ {

View File

@ -12,7 +12,7 @@ import DeltaCore
extension SystemControllerSkinsViewController extension SystemControllerSkinsViewController
{ {
fileprivate enum Section: Int private enum Section: Int
{ {
case portrait case portrait
case landscape case landscape
@ -23,8 +23,8 @@ class SystemControllerSkinsViewController: UITableViewController
{ {
var system: System! var system: System!
@IBOutlet fileprivate var portraitImageView: UIImageView! @IBOutlet private var portraitImageView: UIImageView!
@IBOutlet fileprivate var landscapeImageView: UIImageView! @IBOutlet private var landscapeImageView: UIImageView!
} }
extension SystemControllerSkinsViewController extension SystemControllerSkinsViewController

View File

@ -28,17 +28,17 @@ class ControllerInputsViewController: UIViewController
} }
} }
fileprivate lazy var managedObjectContext: NSManagedObjectContext = DatabaseManager.shared.newBackgroundContext() private lazy var managedObjectContext: NSManagedObjectContext = DatabaseManager.shared.newBackgroundContext()
fileprivate var inputMappings = [System: GameControllerInputMapping]() private var inputMappings = [System: GameControllerInputMapping]()
fileprivate let supportedActionInputs: [ActionInput] = [.quickSave, .quickLoad, .fastForward] private let supportedActionInputs: [ActionInput] = [.quickSave, .quickLoad, .fastForward]
fileprivate var gameViewController: DeltaCore.GameViewController! private var gameViewController: DeltaCore.GameViewController!
fileprivate var actionsMenuViewController: GridMenuViewController! private var actionsMenuViewController: GridMenuViewController!
fileprivate var calloutViews = [AnyInput: InputCalloutView]() private var calloutViews = [AnyInput: InputCalloutView]()
fileprivate var activeCalloutView: InputCalloutView? private var activeCalloutView: InputCalloutView?
@IBOutlet private var actionsMenuViewControllerHeightConstraint: NSLayoutConstraint! @IBOutlet private var actionsMenuViewControllerHeightConstraint: NSLayoutConstraint!
@IBOutlet private var cancelTapGestureRecognizer: UITapGestureRecognizer! @IBOutlet private var cancelTapGestureRecognizer: UITapGestureRecognizer!

View File

@ -13,7 +13,7 @@ import Roxas
extension ControllersSettingsViewController extension ControllersSettingsViewController
{ {
fileprivate enum Section: Int private enum Section: Int
{ {
case none case none
case localDevice case localDevice
@ -46,7 +46,7 @@ class ControllersSettingsViewController: UITableViewController
} }
} }
fileprivate var gameController: GameController? { private var gameController: GameController? {
didSet { didSet {
// Order matters since localDeviceController changes Settings.localControllerPlayerIndex, which sends out NSNotification. // Order matters since localDeviceController changes Settings.localControllerPlayerIndex, which sends out NSNotification.
if oldValue == self.localDeviceController if oldValue == self.localDeviceController
@ -62,9 +62,9 @@ class ControllersSettingsViewController: UITableViewController
} }
} }
fileprivate var connectedControllers = ExternalGameControllerManager.shared.connectedControllers.sorted(by: { $0.playerIndex ?? NSIntegerMax < $1.playerIndex ?? NSIntegerMax }) private var connectedControllers = ExternalGameControllerManager.shared.connectedControllers.sorted(by: { $0.playerIndex ?? NSIntegerMax < $1.playerIndex ?? NSIntegerMax })
fileprivate lazy var localDeviceController: LocalDeviceController = { private lazy var localDeviceController: LocalDeviceController = {
let device = LocalDeviceController() let device = LocalDeviceController()
device.playerIndex = Settings.localControllerPlayerIndex device.playerIndex = Settings.localControllerPlayerIndex
@ -174,7 +174,7 @@ private extension ControllersSettingsViewController
private extension ControllersSettingsViewController private extension ControllersSettingsViewController
{ {
dynamic func externalGameControllerDidConnect(_ notification: Notification) @objc func externalGameControllerDidConnect(_ notification: Notification)
{ {
guard let controller = notification.object as? GameController else { return } guard let controller = notification.object as? GameController else { return }
@ -217,7 +217,7 @@ private extension ControllersSettingsViewController
} }
} }
dynamic func externalGameControllerDidDisconnect(_ notification: Notification) @objc func externalGameControllerDidDisconnect(_ notification: Notification)
{ {
guard let controller = notification.object as? GameController else { return } guard let controller = notification.object as? GameController else { return }

View File

@ -33,7 +33,7 @@ class InputCalloutView: SMCalloutView
} }
} }
fileprivate let textLabel: UILabel private let textLabel: UILabel
init() init()
{ {

View File

@ -12,7 +12,7 @@ import DeltaCore
import Roxas import Roxas
fileprivate extension SettingsViewController private extension SettingsViewController
{ {
enum Section: Int enum Section: Int
{ {
@ -37,12 +37,12 @@ fileprivate extension SettingsViewController
class SettingsViewController: UITableViewController class SettingsViewController: UITableViewController
{ {
@IBOutlet fileprivate var controllerOpacityLabel: UILabel! @IBOutlet private var controllerOpacityLabel: UILabel!
@IBOutlet fileprivate var controllerOpacitySlider: UISlider! @IBOutlet private var controllerOpacitySlider: UISlider!
fileprivate var selectionFeedbackGenerator: UISelectionFeedbackGenerator? private var selectionFeedbackGenerator: UISelectionFeedbackGenerator?
fileprivate var previousSelectedRowIndexPath: IndexPath? private var previousSelectedRowIndexPath: IndexPath?
required init?(coder aDecoder: NSCoder) required init?(coder aDecoder: NSCoder)
{ {
@ -153,12 +153,12 @@ private extension SettingsViewController
private extension SettingsViewController private extension SettingsViewController
{ {
dynamic func externalGameControllerDidConnect(_ notification: Notification) @objc func externalGameControllerDidConnect(_ notification: Notification)
{ {
self.tableView.reloadSections(IndexSet(integer: Section.controllers.rawValue), with: .none) self.tableView.reloadSections(IndexSet(integer: Section.controllers.rawValue), with: .none)
} }
dynamic func externalGameControllerDidDisconnect(_ notification: Notification) @objc func externalGameControllerDidDisconnect(_ notification: Notification)
{ {
self.tableView.reloadSections(IndexSet(integer: Section.controllers.rawValue), with: .none) self.tableView.reloadSections(IndexSet(integer: Section.controllers.rawValue), with: .none)
} }

12
Podfile
View File

@ -10,16 +10,4 @@ target 'Delta' do
pod 'Fabric', '~> 1.6.0' pod 'Fabric', '~> 1.6.0'
pod 'Crashlytics', '~> 3.8.0' pod 'Crashlytics', '~> 3.8.0'
pod 'SMCalloutView' pod 'SMCalloutView'
end
post_install do |installer|
Dir.glob('Pods/SQLite.swift/Sources/SQLite/**/*.swift').each { |path|
begin
text = File.read(path)
text = text.gsub(/import CSQLite/, 'import SQLite3')
File.open(path, 'w') { |file| file.puts text }
rescue Exception
puts "Unable to patch #{path}: #{$!}"
end
}
end end

View File

@ -1,15 +1,15 @@
PODS: PODS:
- Crashlytics (3.8.4): - Crashlytics (3.8.6):
- Fabric (~> 1.6.3) - Fabric (~> 1.6.3)
- Fabric (1.6.11) - Fabric (1.6.13)
- FileMD5Hash (2.0.0) - FileMD5Hash (2.0.0)
- SDWebImage (3.8.2): - SDWebImage (3.8.2):
- SDWebImage/Core (= 3.8.2) - SDWebImage/Core (= 3.8.2)
- SDWebImage/Core (3.8.2) - SDWebImage/Core (3.8.2)
- SMCalloutView (2.1.5) - SMCalloutView (2.1.5)
- SQLite.swift (0.11.3): - SQLite.swift (0.11.4):
- SQLite.swift/standard (= 0.11.3) - SQLite.swift/standard (= 0.11.4)
- SQLite.swift/standard (0.11.3) - SQLite.swift/standard (0.11.4)
DEPENDENCIES: DEPENDENCIES:
- Crashlytics (~> 3.8.0) - Crashlytics (~> 3.8.0)
@ -20,13 +20,13 @@ DEPENDENCIES:
- SQLite.swift (~> 0.11.0) - SQLite.swift (~> 0.11.0)
SPEC CHECKSUMS: SPEC CHECKSUMS:
Crashlytics: 79e236942ca1e7fc641df1feb9a275360a78ab6a Crashlytics: 95d05f4e4c19a771250c4bd9ce344d996de32bbf
Fabric: 5911403591946b8228ab1c51d98f1d7137e863c6 Fabric: 2fb5676bc811af011a04513451f463dac6803206
FileMD5Hash: 3ed69cc19a21ff4d30ae8833fc104275ad2c7de0 FileMD5Hash: 3ed69cc19a21ff4d30ae8833fc104275ad2c7de0
SDWebImage: '098e97e6176540799c27e804c96653ee0833d13c' SDWebImage: '098e97e6176540799c27e804c96653ee0833d13c'
SMCalloutView: 5c0ee363dc8e7204b2fda17dfad38c93e9e23481 SMCalloutView: 5c0ee363dc8e7204b2fda17dfad38c93e9e23481
SQLite.swift: 99b36c22084427f0abbeb957556ce1528cf10bb3 SQLite.swift: 3e3bee21da701b5b9f87c4a672cb54f233505692
PODFILE CHECKSUM: 598f830560ac5b18bbe0eb40134a1719f38f12f1 PODFILE CHECKSUM: a1fb0ce1f1bb0e73380460cc4f946d297a4d5ff1
COCOAPODS: 1.2.1 COCOAPODS: 1.2.1

View File

@ -1,6 +1,6 @@
![Crashlytics Header](https://docs.fabric.io/ios/cocoapod-readmes/cocoapods-crashlytics-header.png) ![Crashlytics Header](https://docs.fabric.io/ios/cocoapod-readmes/cocoapods-crashlytics-header.png)
Part of [Twitter Fabric](https://www.fabric.io), [Crashlytics](http://try.crashlytics.com/) offers the most powerful, yet lightest weight crash reporting solution for iOS. Crashlytics also provides real-time analytics through [Answers](https://answers.io/) and app distributions to testers using [Beta](http://try.crashlytics.com/beta/). Part of [Google Fabric](https://get.fabric.io), [Crashlytics](http://try.crashlytics.com/) offers the most powerful, yet lightest weight crash reporting solution for iOS. Crashlytics also provides real-time analytics through [Answers](https://answers.io/) and app distributions to testers using [Beta](http://try.crashlytics.com/beta/).
## Setup ## Setup
@ -33,8 +33,7 @@ Part of [Twitter Fabric](https://www.fabric.io), [Crashlytics](http://try.crashl
## Resources ## Resources
* [Documentation](https://docs.fabric.io/ios/crashlytics/index.html) * [Documentation](https://docs.fabric.io/apple/crashlytics/overview.html)
* [Forums](https://twittercommunity.com/c/fabric/crashlytics) * [Forums](https://stackoverflow.com/questions/tagged/google-fabric)
* [Website](http://try.crashlytics.com/) * [Website](http://try.crashlytics.com/)
* Follow us on Twitter: [@fabric](https://twitter.com/fabric) and [@crashlytics](https://twitter.com/crashlytics) * Follow us on Twitter: [@fabric](https://twitter.com/fabric) and [@crashlytics](https://twitter.com/crashlytics)
* Follow us on Periscope: [Fabric](https://periscope.tv/fabric) and [TwitterDev](https://periscope.tv/twitterdev)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Pods/Crashlytics/submit generated

Binary file not shown.

7
Pods/Fabric/README.md generated
View File

@ -4,7 +4,7 @@
## Overview ## Overview
[Fabric](https://www.fabric.io) provides developers with the tools they need to build the best apps. Developed and maintained by Twitter and the team that built Crashlytics, Fabric provides an easy way to manage all your SDKs so that youll never have to worry about tedious configurations or juggling different accounts. We let you get right into coding and building the next big app. [Fabric](https://get.fabric.io) provides developers with the tools they need to build the best apps. Developed and maintained by Google and the team that built Crashlytics, Fabric provides an easy way to manage all your SDKs so that youll never have to worry about tedious configurations or juggling different accounts. We let you get right into coding and building the next big app.
For a full list of SDK provided through Fabric visit [https://fabric.io/kits](https://fabric.io/kits). For a full list of SDK provided through Fabric visit [https://fabric.io/kits](https://fabric.io/kits).
@ -37,7 +37,6 @@ The Fabric Pod is a dependency for all Fabric SDKs and is included when installi
## Resources ## Resources
* [Documentation](https://docs.fabric.io/) * [Documentation](https://docs.fabric.io/)
* [Forums](https://twittercommunity.com/c/fabric) * [Forums](https://stackoverflow.com/questions/tagged/google-fabric)
* [Website](https://www.fabric.io) * [Website](https://get.fabric.io)
* Follow us on Twitter: [@fabric](https://twitter.com/fabric) * Follow us on Twitter: [@fabric](https://twitter.com/fabric)
* Follow us on Periscope: [Fabric](https://periscope.tv/fabric) and [TwitterDev](https://periscope.tv/twitterdev)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Pods/Fabric/uploadDSYM generated

Binary file not shown.

18
Pods/Manifest.lock generated
View File

@ -1,15 +1,15 @@
PODS: PODS:
- Crashlytics (3.8.4): - Crashlytics (3.8.6):
- Fabric (~> 1.6.3) - Fabric (~> 1.6.3)
- Fabric (1.6.11) - Fabric (1.6.13)
- FileMD5Hash (2.0.0) - FileMD5Hash (2.0.0)
- SDWebImage (3.8.2): - SDWebImage (3.8.2):
- SDWebImage/Core (= 3.8.2) - SDWebImage/Core (= 3.8.2)
- SDWebImage/Core (3.8.2) - SDWebImage/Core (3.8.2)
- SMCalloutView (2.1.5) - SMCalloutView (2.1.5)
- SQLite.swift (0.11.3): - SQLite.swift (0.11.4):
- SQLite.swift/standard (= 0.11.3) - SQLite.swift/standard (= 0.11.4)
- SQLite.swift/standard (0.11.3) - SQLite.swift/standard (0.11.4)
DEPENDENCIES: DEPENDENCIES:
- Crashlytics (~> 3.8.0) - Crashlytics (~> 3.8.0)
@ -20,13 +20,13 @@ DEPENDENCIES:
- SQLite.swift (~> 0.11.0) - SQLite.swift (~> 0.11.0)
SPEC CHECKSUMS: SPEC CHECKSUMS:
Crashlytics: 79e236942ca1e7fc641df1feb9a275360a78ab6a Crashlytics: 95d05f4e4c19a771250c4bd9ce344d996de32bbf
Fabric: 5911403591946b8228ab1c51d98f1d7137e863c6 Fabric: 2fb5676bc811af011a04513451f463dac6803206
FileMD5Hash: 3ed69cc19a21ff4d30ae8833fc104275ad2c7de0 FileMD5Hash: 3ed69cc19a21ff4d30ae8833fc104275ad2c7de0
SDWebImage: '098e97e6176540799c27e804c96653ee0833d13c' SDWebImage: '098e97e6176540799c27e804c96653ee0833d13c'
SMCalloutView: 5c0ee363dc8e7204b2fda17dfad38c93e9e23481 SMCalloutView: 5c0ee363dc8e7204b2fda17dfad38c93e9e23481
SQLite.swift: 99b36c22084427f0abbeb957556ce1528cf10bb3 SQLite.swift: 3e3bee21da701b5b9f87c4a672cb54f233505692
PODFILE CHECKSUM: 598f830560ac5b18bbe0eb40134a1719f38f12f1 PODFILE CHECKSUM: a1fb0ce1f1bb0e73380460cc4f946d297a4d5ff1
COCOAPODS: 1.2.1 COCOAPODS: 1.2.1

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +0,0 @@
module CSQLite [system] {
header "/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk/usr/include/sqlite3.h"
export *
}

View File

@ -1,4 +0,0 @@
module CSQLite [system] {
header "/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk/usr/include/sqlite3.h"
export *
}

View File

@ -1,4 +0,0 @@
module CSQLite [system] {
header "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.0.sdk/usr/include/sqlite3.h"
export *
}

View File

@ -1,4 +0,0 @@
module CSQLite [system] {
header "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/sqlite3.h"
export *
}

View File

@ -1,4 +0,0 @@
module CSQLite [system] {
header "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator10.0.sdk/usr/include/sqlite3.h"
export *
}

View File

@ -1,4 +0,0 @@
module CSQLite [system] {
header "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include/sqlite3.h"
export *
}

View File

@ -1,4 +0,0 @@
module CSQLite [system] {
header "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/include/sqlite3.h"
export *
}

View File

@ -1,4 +0,0 @@
module CSQLite [system] {
header "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/usr/include/sqlite3.h"
export *
}

View File

@ -1,4 +0,0 @@
module CSQLite [system] {
header "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sqlite3.h"
export *
}

View File

@ -1,4 +0,0 @@
module CSQLite [system] {
header "/Applications/Xcode.app/Contents/Developer/Platforms/WatchOS.platform/Developer/SDKs/WatchOS.sdk/usr/include/sqlite3.h"
export *
}

View File

@ -1,4 +0,0 @@
module CSQLite [system] {
header "/Applications/Xcode.app/Contents/Developer/Platforms/WatchSimulator.platform/Developer/SDKs/WatchSimulator.sdk/usr/include/sqlite3.h"
export *
}

View File

@ -1,19 +1,12 @@
# SQLite.swift # SQLite.swift
[![Build Status][Badge]][Travis] [![CocoaPods Version](https://cocoapod-badges.herokuapp.com/v/SQLite.swift/badge.png)](http://cocoadocs.org/docsets/SQLite.swift) [![Swift](https://img.shields.io/badge/swift-3-orange.svg?style=flat)](https://developer.apple.com/swift/) [![Platform](https://cocoapod-badges.herokuapp.com/p/SQLite.swift/badge.png)](http://cocoadocs.org/docsets/SQLite.swift) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![Join the chat at https://gitter.im/stephencelis/SQLite.swift](https://badges.gitter.im/stephencelis/SQLite.swift.svg)](https://gitter.im/stephencelis/SQLite.swift) [![Build Status][TravisBadge]][TravisLink] [![CocoaPods Version][CocoaPodsVersionBadge]][CocoaPodsVersionLink] [![Swift4 compatible][Swift4Badge]][Swift4Link] [![Platform][PlatformBadge]][PlatformLink] [![Carthage compatible][CartagheBadge]][CarthageLink] [![Join the chat at https://gitter.im/stephencelis/SQLite.swift][GitterBadge]][GitterLink]
A type-safe, [Swift][]-language layer over [SQLite3][]. A type-safe, [Swift][]-language layer over [SQLite3][].
[SQLite.swift][] provides compile-time confidence in SQL statement [SQLite.swift][] provides compile-time confidence in SQL statement
syntax _and_ intent. syntax _and_ intent.
[Badge]: https://img.shields.io/travis/stephencelis/SQLite.swift/master.svg?style=flat
[Travis]: https://travis-ci.org/stephencelis/SQLite.swift
[Swift]: https://developer.apple.com/swift/
[SQLite3]: http://www.sqlite.org
[SQLite.swift]: https://github.com/stephencelis/SQLite.swift
## Features ## Features
- A pure-Swift interface - A pure-Swift interface
@ -25,16 +18,20 @@ syntax _and_ intent.
- [Full-text search][] support - [Full-text search][] support
- [Well-documented][See Documentation] - [Well-documented][See Documentation]
- Extensively tested - Extensively tested
- SQLCipher support via CocoaPods - [SQLCipher][] support via CocoaPods
- Active support at [StackOverflow](http://stackoverflow.com/questions/tagged/sqlite.swift), and [Gitter Chat Room](https://gitter.im/stephencelis/SQLite.swift) (_experimental_) - Active support at
[StackOverflow](http://stackoverflow.com/questions/tagged/sqlite.swift),
and [Gitter Chat Room](https://gitter.im/stephencelis/SQLite.swift)
(_experimental_)
[SQLCipher]: https://www.zetetic.net/sqlcipher/
[Full-text search]: Documentation/Index.md#full-text-search [Full-text search]: Documentation/Index.md#full-text-search
[See Documentation]: Documentation/Index.md#sqliteswift-documentation [See Documentation]: Documentation/Index.md#sqliteswift-documentation
## Usage ## Usage
``` swift ```swift
import SQLite import SQLite
let db = try Connection("path/to/db.sqlite3") let db = try Connection("path/to/db.sqlite3")
@ -74,14 +71,14 @@ try db.run(alice.update(email <- email.replace("mac.com", with: "me.com")))
try db.run(alice.delete()) try db.run(alice.delete())
// DELETE FROM "users" WHERE ("id" = 1) // DELETE FROM "users" WHERE ("id" = 1)
db.scalar(users.count) // 0 try db.scalar(users.count) // 0
// SELECT count(*) FROM "users" // SELECT count(*) FROM "users"
``` ```
SQLite.swift also works as a lightweight, Swift-friendly wrapper over the C SQLite.swift also works as a lightweight, Swift-friendly wrapper over the C
API. API.
``` swift ```swift
let stmt = try db.prepare("INSERT INTO users (email) VALUES (?)") let stmt = try db.prepare("INSERT INTO users (email) VALUES (?)")
for email in ["betty@icloud.com", "cathy@icloud.com"] { for email in ["betty@icloud.com", "cathy@icloud.com"] {
try stmt.run(email) try stmt.run(email)
@ -97,7 +94,7 @@ for row in try db.prepare("SELECT id, email FROM users") {
// id: Optional(3), email: Optional("cathy@icloud.com") // id: Optional(3), email: Optional("cathy@icloud.com")
} }
db.scalar("SELECT count(*) FROM users") // 2 try db.scalar("SELECT count(*) FROM users") // 2
``` ```
[Read the documentation][See Documentation] or explore more, [Read the documentation][See Documentation] or explore more,
@ -105,13 +102,17 @@ interactively, from the Xcode projects playground.
![SQLite.playground Screen Shot](Documentation/Resources/playground@2x.png) ![SQLite.playground Screen Shot](Documentation/Resources/playground@2x.png)
For a more comprehensive example, see [this article](http://masteringswift.blogspot.com/2015/09/create-data-access-layer-with.html) and the [companion repository](https://github.com/hoffmanjon/SQLiteDataAccessLayer2/tree/master). For a more comprehensive example, see
[this article][Create a Data Access Layer with SQLite.swift and Swift 2]
and the [companion repository][SQLiteDataAccessLayer2].
[Create a Data Access Layer with SQLite.swift and Swift 2]: http://masteringswift.blogspot.com/2015/09/create-data-access-layer-with.html
[SQLiteDataAccessLayer2]: https://github.com/hoffmanjon/SQLiteDataAccessLayer2/tree/master
## Installation ## Installation
> _Note:_ SQLite.swift requires Swift 3 (and [Xcode][] 8) or greater. If you absolutely > _Note:_ SQLite.swift requires Swift 4 (and [Xcode][] 9).
> need compatibility with Swift 2.3 you can use the [swift-2.3][] branch or older
> released versions. New development will happen exclusively on the master/Swift 3 branch.
### Carthage ### Carthage
@ -122,11 +123,12 @@ install SQLite.swift with Carthage:
2. Update your Cartfile to include the following: 2. Update your Cartfile to include the following:
``` ```ruby
github "stephencelis/SQLite.swift" ~> 0.11.3 github "stephencelis/SQLite.swift" ~> 0.11.4
``` ```
3. Run `carthage update` and [add the appropriate framework][Carthage Usage]. 3. Run `carthage update` and
[add the appropriate framework][Carthage Usage].
[Carthage]: https://github.com/Carthage/Carthage [Carthage]: https://github.com/Carthage/Carthage
@ -139,51 +141,47 @@ install SQLite.swift with Carthage:
[CocoaPods][] is a dependency manager for Cocoa projects. To install [CocoaPods][] is a dependency manager for Cocoa projects. To install
SQLite.swift with CocoaPods: SQLite.swift with CocoaPods:
1. Verify that your copy of Xcode is installed and active in the default location (`/Applications/Xcode.app`). 1. Make sure CocoaPods is [installed][CocoaPods Installation]. (SQLite.swift
requires version 1.0.0 or greater.)
```sh ```sh
sudo xcode-select --switch /Applications/Xcode.app
```
2. Make sure CocoaPods is [installed][CocoaPods Installation]. (SQLite.swift requires version 1.0.0 or greater.)
``` sh
# Using the default Ruby install will require you to use sudo when # Using the default Ruby install will require you to use sudo when
# installing and updating gems. # installing and updating gems.
[sudo] gem install cocoapods [sudo] gem install cocoapods
``` ```
3. Update your Podfile to include the following: 2. Update your Podfile to include the following:
``` ruby ```ruby
use_frameworks! use_frameworks!
target 'YourAppTargetName' do target 'YourAppTargetName' do
pod 'SQLite.swift', '~> 0.11.3' pod 'SQLite.swift', '~> 0.11.4'
end end
``` ```
4. Run `pod install --repo-update`. 3. Run `pod install --repo-update`.
[CocoaPods]: https://cocoapods.org [CocoaPods]: https://cocoapods.org
[CocoaPods Installation]: https://guides.cocoapods.org/using/getting-started.html#getting-started [CocoaPods Installation]: https://guides.cocoapods.org/using/getting-started.html#getting-started
### Swift Package Manager ### Swift Package Manager
The [Swift Package Manager][] is a tool for managing the distribution of Swift code. The [Swift Package Manager][] is a tool for managing the distribution of
Swift code.
1. Add the following to your `Package.swift` file: 1. Add the following to your `Package.swift` file:
```swift ```swift
dependencies: [ dependencies: [
.Package(url: "https://github.com/stephencelis/SQLite.swift.git", majorVersion: 0, minor: 11) .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.11.4")
] ]
``` ```
2. Build your project: 2. Build your project:
``` sh ```sh
$ swift build -Xlinker -lsqlite3 $ swift build
``` ```
[Swift Package Manager]: https://swift.org/package-manager [Swift Package Manager]: https://swift.org/package-manager
@ -204,9 +202,11 @@ To install SQLite.swift as an Xcode sub-project:
4. **Add**. 4. **Add**.
Some additional steps are required to install the application on an actual device: Some additional steps are required to install the application on an actual
device:
5. In the **General** tab, click the **+** button under **Embedded Binaries**. 5. In the **General** tab, click the **+** button under **Embedded
Binaries**.
6. Select the appropriate **SQLite.framework** for your platform. 6. Select the appropriate **SQLite.framework** for your platform.
@ -251,7 +251,8 @@ file](./LICENSE.txt) for more information.
These projects enhance or use SQLite.swift: These projects enhance or use SQLite.swift:
- [SQLiteMigrationManager.swift](https://github.com/garriguv/SQLiteMigrationManager.swift) (inspired by [FMDBMigrationManager](https://github.com/layerhq/FMDBMigrationManager)) - [SQLiteMigrationManager.swift][] (inspired by
[FMDBMigrationManager][])
## Alternatives ## Alternatives
@ -265,5 +266,28 @@ Looking for something else? Try another Swift wrapper (or [FMDB][]):
- [SwiftData](https://github.com/ryanfowler/SwiftData) - [SwiftData](https://github.com/ryanfowler/SwiftData)
- [SwiftSQLite](https://github.com/chrismsimpson/SwiftSQLite) - [SwiftSQLite](https://github.com/chrismsimpson/SwiftSQLite)
[Swift]: https://swift.org/
[SQLite3]: http://www.sqlite.org
[SQLite.swift]: https://github.com/stephencelis/SQLite.swift
[TravisBadge]: https://img.shields.io/travis/stephencelis/SQLite.swift/master.svg?style=flat
[TravisLink]: https://travis-ci.org/stephencelis/SQLite.swift
[CocoaPodsVersionBadge]: https://cocoapod-badges.herokuapp.com/v/SQLite.swift/badge.png
[CocoaPodsVersionLink]: http://cocoadocs.org/docsets/SQLite.swift
[PlatformBadge]: https://cocoapod-badges.herokuapp.com/p/SQLite.swift/badge.png
[PlatformLink]: http://cocoadocs.org/docsets/SQLite.swift
[CartagheBadge]: https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat
[CarthageLink]: https://github.com/Carthage/Carthage
[GitterBadge]: https://badges.gitter.im/stephencelis/SQLite.swift.svg
[GitterLink]: https://gitter.im/stephencelis/SQLite.swift
[Swift4Badge]: https://img.shields.io/badge/swift-4-orange.svg?style=flat
[Swift4Link]: https://developer.apple.com/swift/
[SQLiteMigrationManager.swift]: https://github.com/garriguv/SQLiteMigrationManager.swift
[FMDB]: https://github.com/ccgus/fmdb [FMDB]: https://github.com/ccgus/fmdb
[swift-2.3]: https://github.com/stephencelis/SQLite.swift/tree/swift-2.3 [FMDBMigrationManager]: https://github.com/layerhq/FMDBMigrationManager

View File

@ -22,13 +22,15 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
import Foundation.NSUUID import Foundation
import Dispatch import Dispatch
#if SQLITE_SWIFT_STANDALONE #if SQLITE_SWIFT_STANDALONE
import sqlite3 import sqlite3
#elseif SQLITE_SWIFT_SQLCIPHER #elseif SQLITE_SWIFT_SQLCIPHER
import SQLCipher import SQLCipher
#elseif SWIFT_PACKAGE || COCOAPODS #elseif os(Linux)
import CSQLite
#else
import SQLite3 import SQLite3
#endif #endif
@ -38,12 +40,12 @@ public final class Connection {
/// The location of a SQLite database. /// The location of a SQLite database.
public enum Location { public enum Location {
/// An in-memory database (equivalent to `.URI(":memory:")`). /// An in-memory database (equivalent to `.uri(":memory:")`).
/// ///
/// See: <https://www.sqlite.org/inmemorydb.html#sharedmemdb> /// See: <https://www.sqlite.org/inmemorydb.html#sharedmemdb>
case inMemory case inMemory
/// A temporary, file-backed database (equivalent to `.URI("")`). /// A temporary, file-backed database (equivalent to `.uri("")`).
/// ///
/// See: <https://www.sqlite.org/inmemorydb.html#temp_db> /// See: <https://www.sqlite.org/inmemorydb.html#temp_db>
case temporary case temporary
@ -93,7 +95,7 @@ public final class Connection {
/// - location: The location of the database. Creates a new database if it /// - location: The location of the database. Creates a new database if it
/// doesnt already exist (unless in read-only mode). /// doesnt already exist (unless in read-only mode).
/// ///
/// Default: `.InMemory`. /// Default: `.inMemory`.
/// ///
/// - readonly: Whether or not to open the database in a read-only state. /// - readonly: Whether or not to open the database in a read-only state.
/// ///
@ -321,14 +323,14 @@ public final class Connection {
/// ///
/// - mode: The mode in which a transaction acquires a lock. /// - mode: The mode in which a transaction acquires a lock.
/// ///
/// Default: `.Deferred` /// Default: `.deferred`
/// ///
/// - block: A closure to run SQL statements within the transaction. /// - block: A closure to run SQL statements within the transaction.
/// The transaction will be committed when the block returns. The block /// The transaction will be committed when the block returns. The block
/// must throw to roll the transaction back. /// must throw to roll the transaction back.
/// ///
/// - Throws: `Result.Error`, and rethrows. /// - Throws: `Result.Error`, and rethrows.
public func transaction(_ mode: TransactionMode = .deferred, block: @escaping () throws -> Void) throws { public func transaction(_ mode: TransactionMode = .deferred, block: () throws -> Void) throws {
try transaction("BEGIN \(mode.rawValue) TRANSACTION", block, "COMMIT TRANSACTION", or: "ROLLBACK TRANSACTION") try transaction("BEGIN \(mode.rawValue) TRANSACTION", block, "COMMIT TRANSACTION", or: "ROLLBACK TRANSACTION")
} }
@ -348,23 +350,23 @@ public final class Connection {
/// The block must throw to roll the savepoint back. /// The block must throw to roll the savepoint back.
/// ///
/// - Throws: `SQLite.Result.Error`, and rethrows. /// - Throws: `SQLite.Result.Error`, and rethrows.
public func savepoint(_ name: String = UUID().uuidString, block: @escaping () throws -> Void) throws { public func savepoint(_ name: String = UUID().uuidString, block: () throws -> Void) throws {
let name = name.quote("'") let name = name.quote("'")
let savepoint = "SAVEPOINT \(name)" let savepoint = "SAVEPOINT \(name)"
try transaction(savepoint, block, "RELEASE \(savepoint)", or: "ROLLBACK TO \(savepoint)") try transaction(savepoint, block, "RELEASE \(savepoint)", or: "ROLLBACK TO \(savepoint)")
} }
fileprivate func transaction(_ begin: String, _ block: @escaping () throws -> Void, _ commit: String, or rollback: String) throws { fileprivate func transaction(_ begin: String, _ block: () throws -> Void, _ commit: String, or rollback: String) throws {
return try sync { return try sync {
try self.run(begin) try self.run(begin)
do { do {
try block() try block()
try self.run(commit)
} catch { } catch {
try self.run(rollback) try self.run(rollback)
throw error throw error
} }
try self.run(commit)
} }
} }
@ -413,7 +415,7 @@ public final class Connection {
/// ///
/// db.trace { SQL in print(SQL) } /// db.trace { SQL in print(SQL) }
public func trace(_ callback: ((String) -> Void)?) { public func trace(_ callback: ((String) -> Void)?) {
#if SQLITE_SWIFT_SQLCIPHER #if SQLITE_SWIFT_SQLCIPHER || os(Linux)
trace_v1(callback) trace_v1(callback)
#else #else
if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) { if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) {
@ -583,9 +585,11 @@ public final class Connection {
} }
} }
var flags = SQLITE_UTF8 var flags = SQLITE_UTF8
#if !os(Linux)
if deterministic { if deterministic {
flags |= SQLITE_DETERMINISTIC flags |= SQLITE_DETERMINISTIC
} }
#endif
sqlite3_create_function_v2(handle, function, Int32(argc), flags, unsafeBitCast(box, to: UnsafeMutableRawPointer.self), { context, argc, value in sqlite3_create_function_v2(handle, function, Int32(argc), flags, unsafeBitCast(box, to: UnsafeMutableRawPointer.self), { context, argc, value in
let function = unsafeBitCast(sqlite3_user_data(context), to: Function.self) let function = unsafeBitCast(sqlite3_user_data(context), to: Function.self)
function(context, argc, value) function(context, argc, value)
@ -626,29 +630,12 @@ public final class Connection {
// MARK: - Error Handling // MARK: - Error Handling
func sync<T>(_ block: @escaping () throws -> T) rethrows -> T { func sync<T>(_ block: () throws -> T) rethrows -> T {
var success: T?
var failure: Error?
let box: () -> Void = {
do {
success = try block()
} catch {
failure = error
}
}
if DispatchQueue.getSpecific(key: Connection.queueKey) == queueContext { if DispatchQueue.getSpecific(key: Connection.queueKey) == queueContext {
box() return try block()
} else { } else {
queue.sync(execute: box) // FIXME: rdar://problem/21389236 return try queue.sync(execute: block)
} }
if let failure = failure {
try { () -> Void in throw failure }()
}
return success!
} }
@discardableResult func check(_ resultCode: Int32, statement: Statement? = nil) throws -> Int32 { @discardableResult func check(_ resultCode: Int32, statement: Statement? = nil) throws -> Int32 {
@ -694,6 +681,13 @@ public enum Result : Error {
fileprivate static let successCodes: Set = [SQLITE_OK, SQLITE_ROW, SQLITE_DONE] fileprivate static let successCodes: Set = [SQLITE_OK, SQLITE_ROW, SQLITE_DONE]
/// Represents a SQLite specific [error code](https://sqlite.org/rescode.html)
///
/// - message: English-language text that describes the error
///
/// - code: SQLite [error code](https://sqlite.org/rescode.html#primary_result_code_list)
///
/// - statement: the statement which produced the error
case error(message: String, code: Int32, statement: Statement?) case error(message: String, code: Int32, statement: Statement?)
init?(errorCode: Int32, connection: Connection, statement: Statement? = nil) { init?(errorCode: Int32, connection: Connection, statement: Statement? = nil) {
@ -719,7 +713,7 @@ extension Result : CustomStringConvertible {
} }
} }
#if !SQLITE_SWIFT_SQLCIPHER #if !SQLITE_SWIFT_SQLCIPHER && !os(Linux)
@available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) @available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *)
extension Connection { extension Connection {
fileprivate func trace_v2(_ callback: ((String) -> Void)?) { fileprivate func trace_v2(_ callback: ((String) -> Void)?) {

View File

@ -0,0 +1,21 @@
import Foundation
public enum QueryError: Error, CustomStringConvertible {
case noSuchTable(name: String)
case noSuchColumn(name: String, columns: [String])
case ambiguousColumn(name: String, similar: [String])
case unexpectedNullValue(name: String)
public var description: String {
switch self {
case .noSuchTable(let name):
return "No such table: \(name)"
case .noSuchColumn(let name, let columns):
return "No such column `\(name)` in columns \(columns)"
case .ambiguousColumn(let name, let similar):
return "Ambiguous column `\(name)` (please disambiguate: \(similar))"
case .unexpectedNullValue(let name):
return "Unexpected null value for column `\(name)`"
}
}
}

View File

@ -26,7 +26,9 @@
import sqlite3 import sqlite3
#elseif SQLITE_SWIFT_SQLCIPHER #elseif SQLITE_SWIFT_SQLCIPHER
import SQLCipher import SQLCipher
#elseif SWIFT_PACKAGE || COCOAPODS #elseif os(Linux)
import CSQLite
#else
import SQLite3 import SQLite3
#endif #endif
@ -200,12 +202,30 @@ extension Statement : Sequence {
} }
extension Statement : IteratorProtocol { public protocol FailableIterator : IteratorProtocol {
func failableNext() throws -> Self.Element?
}
public func next() -> [Binding?]? { extension FailableIterator {
return try! step() ? Array(row) : nil public func next() -> Element? {
return try! failableNext()
} }
}
extension Array {
public init<I: FailableIterator>(_ failableIterator: I) throws where I.Element == Element {
self.init()
while let row = try failableIterator.failableNext() {
append(row)
}
}
}
extension Statement : FailableIterator {
public typealias Element = [Binding?]
public func failableNext() throws -> [Binding?]? {
return try step() ? Array(row) : nil
}
} }
extension Statement : CustomStringConvertible { extension Statement : CustomStringConvertible {

View File

@ -151,13 +151,18 @@ extension Connection {
guard let (token, range) = next(string) else { return nil } guard let (token, range) = next(string) else { return nil }
let view = string.utf8 let view:String.UTF8View = string.utf8
offset.pointee += Int32(string.substring(to: range.lowerBound).utf8.count)
length.pointee = Int32(view.distance(from: range.lowerBound.samePosition(in: view), to: range.upperBound.samePosition(in: view))) if let from = range.lowerBound.samePosition(in: view),
return token let to = range.upperBound.samePosition(in: view) {
offset.pointee += Int32(string[string.startIndex..<range.lowerBound].utf8.count)
length.pointee = Int32(view.distance(from: from, to: to))
return token
} else {
return nil
}
}) })
} }
} }
/// Configuration options shared between the [FTS4](https://www.sqlite.org/fts3.html) and /// Configuration options shared between the [FTS4](https://www.sqlite.org/fts3.html) and

View File

@ -68,41 +68,3 @@ public var dateFormatter: DateFormatter = {
formatter.timeZone = TimeZone(secondsFromGMT: 0) formatter.timeZone = TimeZone(secondsFromGMT: 0)
return formatter return formatter
}() }()
// FIXME: rdar://problem/18673897 // subscript<T>
extension QueryType {
public subscript(column: Expression<Data>) -> Expression<Data> {
return namespace(column)
}
public subscript(column: Expression<Data?>) -> Expression<Data?> {
return namespace(column)
}
public subscript(column: Expression<Date>) -> Expression<Date> {
return namespace(column)
}
public subscript(column: Expression<Date?>) -> Expression<Date?> {
return namespace(column)
}
}
extension Row {
public subscript(column: Expression<Data>) -> Data {
return get(column)
}
public subscript(column: Expression<Data?>) -> Data? {
return get(column)
}
public subscript(column: Expression<Date>) -> Date {
return get(column)
}
public subscript(column: Expression<Date?>) -> Date? {
return get(column)
}
}

View File

@ -26,7 +26,9 @@
import sqlite3 import sqlite3
#elseif SQLITE_SWIFT_SQLCIPHER #elseif SQLITE_SWIFT_SQLCIPHER
import SQLCipher import SQLCipher
#elseif SWIFT_PACKAGE || COCOAPODS #elseif os(Linux)
import CSQLite
#else
import SQLite3 import SQLite3
#endif #endif

View File

@ -0,0 +1,340 @@
//
// SQLite.swift
// https://github.com/stephencelis/SQLite.swift
// Copyright © 2014-2015 Stephen Celis.
//
// 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
extension QueryType {
/// Creates an `INSERT` statement by encoding the given object
/// This method converts any custom nested types to JSON data and does not handle any sort
/// of object relationships. If you want to support relationships between objects you will
/// have to provide your own Encodable implementations that encode the correct ids.
///
/// - Parameters:
///
/// - encodable: An encodable object to insert
///
/// - userInfo: User info to be passed to encoder
///
/// - otherSetters: Any other setters to include in the insert
///
/// - Returns: An `INSERT` statement fort the encodable object
public func insert(_ encodable: Encodable, userInfo: [CodingUserInfoKey:Any] = [:], otherSetters: [Setter] = []) throws -> Insert {
let encoder = SQLiteEncoder(userInfo: userInfo)
try encodable.encode(to: encoder)
return self.insert(encoder.setters + otherSetters)
}
/// Creates an `UPDATE` statement by encoding the given object
/// This method converts any custom nested types to JSON data and does not handle any sort
/// of object relationships. If you want to support relationships between objects you will
/// have to provide your own Encodable implementations that encode the correct ids.
///
/// - Parameters:
///
/// - encodable: An encodable object to insert
///
/// - userInfo: User info to be passed to encoder
///
/// - otherSetters: Any other setters to include in the insert
///
/// - Returns: An `UPDATE` statement fort the encodable object
public func update(_ encodable: Encodable, userInfo: [CodingUserInfoKey:Any] = [:], otherSetters: [Setter] = []) throws -> Update {
let encoder = SQLiteEncoder(userInfo: userInfo)
try encodable.encode(to: encoder)
return self.update(encoder.setters + otherSetters)
}
}
extension Row {
/// Decode an object from this row
/// This method expects any custom nested types to be in the form of JSON data and does not handle
/// any sort of object relationships. If you want to support relationships between objects you will
/// have to provide your own Decodable implementations that decodes the correct columns.
///
/// - Parameter: userInfo
///
/// - Returns: a decoded object from this row
public func decode<V: Decodable>(userInfo: [CodingUserInfoKey: Any] = [:]) throws -> V {
return try V(from: self.decoder(userInfo: userInfo))
}
public func decoder(userInfo: [CodingUserInfoKey: Any] = [:]) -> Decoder {
return SQLiteDecoder(row: self, userInfo: userInfo)
}
}
/// Generates a list of settings for an Encodable object
fileprivate class SQLiteEncoder: Encoder {
class SQLiteKeyedEncodingContainer<MyKey: CodingKey>: KeyedEncodingContainerProtocol {
typealias Key = MyKey
let encoder: SQLiteEncoder
let codingPath: [CodingKey] = []
init(encoder: SQLiteEncoder) {
self.encoder = encoder
}
func superEncoder() -> Swift.Encoder {
fatalError("SQLiteEncoding does not support super encoders")
}
func superEncoder(forKey key: Key) -> Swift.Encoder {
fatalError("SQLiteEncoding does not support super encoders")
}
func encodeNil(forKey key: SQLiteEncoder.SQLiteKeyedEncodingContainer<Key>.Key) throws {
self.encoder.setters.append(Expression<String?>(key.stringValue) <- nil)
}
func encode(_ value: Int, forKey key: SQLiteEncoder.SQLiteKeyedEncodingContainer<Key>.Key) throws {
self.encoder.setters.append(Expression(key.stringValue) <- value)
}
func encode(_ value: Bool, forKey key: Key) throws {
self.encoder.setters.append(Expression(key.stringValue) <- value)
}
func encode(_ value: Float, forKey key: Key) throws {
self.encoder.setters.append(Expression(key.stringValue) <- Double(value))
}
func encode(_ value: Double, forKey key: Key) throws {
self.encoder.setters.append(Expression(key.stringValue) <- value)
}
func encode(_ value: String, forKey key: Key) throws {
self.encoder.setters.append(Expression(key.stringValue) <- value)
}
func encode<T>(_ value: T, forKey key: Key) throws where T : Swift.Encodable {
if let data = value as? Data {
self.encoder.setters.append(Expression(key.stringValue) <- data)
}
else {
let encoded = try JSONEncoder().encode(value)
let string = String(data: encoded, encoding: .utf8)
self.encoder.setters.append(Expression(key.stringValue) <- string)
}
}
func encode(_ value: Int8, forKey key: Key) throws {
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an Int8 is not supported"))
}
func encode(_ value: Int16, forKey key: Key) throws {
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an Int16 is not supported"))
}
func encode(_ value: Int32, forKey key: Key) throws {
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an Int32 is not supported"))
}
func encode(_ value: Int64, forKey key: Key) throws {
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an Int64 is not supported"))
}
func encode(_ value: UInt, forKey key: Key) throws {
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an UInt is not supported"))
}
func encode(_ value: UInt8, forKey key: Key) throws {
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an UInt8 is not supported"))
}
func encode(_ value: UInt16, forKey key: Key) throws {
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an UInt16 is not supported"))
}
func encode(_ value: UInt32, forKey key: Key) throws {
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an UInt32 is not supported"))
}
func encode(_ value: UInt64, forKey key: Key) throws {
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an UInt64 is not supported"))
}
func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> where NestedKey : CodingKey {
fatalError("encoding a nested container is not supported")
}
func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
fatalError("encoding nested values is not supported")
}
}
fileprivate var setters: [SQLite.Setter] = []
let codingPath: [CodingKey] = []
let userInfo: [CodingUserInfoKey: Any]
init(userInfo: [CodingUserInfoKey: Any]) {
self.userInfo = userInfo
}
func singleValueContainer() -> SingleValueEncodingContainer {
fatalError("not supported")
}
func unkeyedContainer() -> UnkeyedEncodingContainer {
fatalError("not supported")
}
func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key : CodingKey {
return KeyedEncodingContainer(SQLiteKeyedEncodingContainer(encoder: self))
}
}
fileprivate class SQLiteDecoder : Decoder {
class SQLiteKeyedDecodingContainer<MyKey: CodingKey> : KeyedDecodingContainerProtocol {
typealias Key = MyKey
let codingPath: [CodingKey] = []
let row: Row
init(row: Row) {
self.row = row
}
var allKeys: [Key] {
return self.row.columnNames.keys.flatMap({Key(stringValue: $0)})
}
func contains(_ key: Key) -> Bool {
return self.row.hasValue(for: key.stringValue)
}
func decodeNil(forKey key: Key) throws -> Bool {
return !self.contains(key)
}
func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool {
return try self.row.get(Expression(key.stringValue))
}
func decode(_ type: Int.Type, forKey key: Key) throws -> Int {
return try self.row.get(Expression(key.stringValue))
}
func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8 {
throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an Int8 is not supported"))
}
func decode(_ type: Int16.Type, forKey key: Key) throws -> Int16 {
throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an Int16 is not supported"))
}
func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 {
throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an Int32 is not supported"))
}
func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 {
throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an UInt64 is not supported"))
}
func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt {
throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an UInt is not supported"))
}
func decode(_ type: UInt8.Type, forKey key: Key) throws -> UInt8 {
throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an UInt8 is not supported"))
}
func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16 {
throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an UInt16 is not supported"))
}
func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32 {
throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an UInt32 is not supported"))
}
func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64 {
throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an UInt64 is not supported"))
}
func decode(_ type: Float.Type, forKey key: Key) throws -> Float {
return Float(try self.row.get(Expression<Double>(key.stringValue)))
}
func decode(_ type: Double.Type, forKey key: Key) throws -> Double {
return try self.row.get(Expression(key.stringValue))
}
func decode(_ type: String.Type, forKey key: Key) throws -> String {
return try self.row.get(Expression(key.stringValue))
}
func decode<T>(_ type: T.Type, forKey key: Key) throws -> T where T: Swift.Decodable {
if type == Data.self {
let data = try self.row.get(Expression<Data>(key.stringValue))
return data as! T
}
guard let JSONString = try self.row.get(Expression<String?>(key.stringValue)) else {
throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "an unsupported type was found"))
}
guard let data = JSONString.data(using: .utf8) else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "invalid utf8 data found"))
}
return try JSONDecoder().decode(type, from: data)
}
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding nested containers is not supported"))
}
func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding unkeyed containers is not supported"))
}
func superDecoder() throws -> Swift.Decoder {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding super encoders containers is not supported"))
}
func superDecoder(forKey key: Key) throws -> Swift.Decoder {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding super decoders is not supported"))
}
}
let row: Row
let codingPath: [CodingKey] = []
let userInfo: [CodingUserInfoKey: Any]
init(row: Row, userInfo: [CodingUserInfoKey: Any]) {
self.row = row
self.userInfo = userInfo
}
func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> where Key : CodingKey {
return KeyedDecodingContainer(SQLiteKeyedDecodingContainer(row: self.row))
}
func unkeyedContainer() throws -> UnkeyedDecodingContainer {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an unkeyed container is not supported"))
}
func singleValueContainer() throws -> SingleValueDecodingContainer {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding a single value container is not supported"))
}
}

View File

@ -22,7 +22,7 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
import Foundation.NSData import Foundation
extension ExpressionType where UnderlyingType : Number { extension ExpressionType where UnderlyingType : Number {
@ -223,6 +223,31 @@ extension ExpressionType where UnderlyingType == String {
return Expression("(\(template) LIKE ? ESCAPE ?)", bindings + [pattern, String(character)]) return Expression("(\(template) LIKE ? ESCAPE ?)", bindings + [pattern, String(character)])
} }
/// Builds a copy of the expression appended with a `LIKE` query against the
/// given pattern.
///
/// let email = Expression<String>("email")
/// let pattern = Expression<String>("pattern")
/// email.like(pattern)
/// // "email" LIKE "pattern"
///
/// - Parameters:
///
/// - pattern: A pattern to match.
///
/// - escape: An (optional) character designated for escaping
/// pattern-matching characters (*i.e.*, the `%` and `_` characters).
///
/// - Returns: A copy of the expression appended with a `LIKE` query against
/// the given pattern.
public func like(_ pattern: Expression<String>, escape character: Character? = nil) -> Expression<Bool> {
guard let character = character else {
return "LIKE".infix(self, pattern)
}
let like: Expression<Bool> = "LIKE".infix(self, pattern, wrap: false)
return Expression("(\(like.template) ESCAPE ?)", like.bindings + [String(character)])
}
/// Builds a copy of the expression appended with a `GLOB` query against the /// Builds a copy of the expression appended with a `GLOB` query against the
/// given pattern. /// given pattern.
/// ///
@ -422,6 +447,31 @@ extension ExpressionType where UnderlyingType == String? {
} }
return Expression("(\(template) LIKE ? ESCAPE ?)", bindings + [pattern, String(character)]) return Expression("(\(template) LIKE ? ESCAPE ?)", bindings + [pattern, String(character)])
} }
/// Builds a copy of the expression appended with a `LIKE` query against the
/// given pattern.
///
/// let email = Expression<String>("email")
/// let pattern = Expression<String>("pattern")
/// email.like(pattern)
/// // "email" LIKE "pattern"
///
/// - Parameters:
///
/// - pattern: A pattern to match.
///
/// - escape: An (optional) character designated for escaping
/// pattern-matching characters (*i.e.*, the `%` and `_` characters).
///
/// - Returns: A copy of the expression appended with a `LIKE` query against
/// the given pattern.
public func like(_ pattern: Expression<String>, escape character: Character? = nil) -> Expression<Bool?> {
guard let character = character else {
return "LIKE".infix(self, pattern)
}
let like: Expression<Bool> = "LIKE".infix(self, pattern, wrap: false)
return Expression("(\(like.template) ESCAPE ?)", like.bindings + [String(character)])
}
/// Builds a copy of the expression appended with a `GLOB` query against the /// Builds a copy of the expression appended with a `GLOB` query against the
/// given pattern. /// given pattern.
@ -623,6 +673,35 @@ extension Collection where Iterator.Element : Value, IndexDistance == Int {
} }
extension String {
/// Builds a copy of the expression appended with a `LIKE` query against the
/// given pattern.
///
/// let email = "some@thing.com"
/// let pattern = Expression<String>("pattern")
/// email.like(pattern)
/// // 'some@thing.com' LIKE "pattern"
///
/// - Parameters:
///
/// - pattern: A pattern to match.
///
/// - escape: An (optional) character designated for escaping
/// pattern-matching characters (*i.e.*, the `%` and `_` characters).
///
/// - Returns: A copy of the expression appended with a `LIKE` query against
/// the given pattern.
public func like(_ pattern: Expression<String>, escape character: Character? = nil) -> Expression<Bool> {
guard let character = character else {
return "LIKE".infix(self, pattern)
}
let like: Expression<Bool> = "LIKE".infix(self, pattern, wrap: false)
return Expression("(\(like.template) ESCAPE ?)", like.bindings + [String(character)])
}
}
/// Builds a copy of the given expressions wrapped with the `ifnull` function. /// Builds a copy of the given expressions wrapped with the `ifnull` function.
/// ///
/// let name = Expression<String?>("name") /// let name = Expression<String?>("name")

View File

@ -0,0 +1,106 @@
//
// SQLite.swift
// https://github.com/stephencelis/SQLite.swift
// Copyright © 2014-2015 Stephen Celis.
//
// 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
/// All five date and time functions take a time string as an argument.
/// The time string is followed by zero or more modifiers.
/// The strftime() function also takes a format string as its first argument.
///
/// https://www.sqlite.org/lang_datefunc.html
public class DateFunctions {
/// The date() function returns the date in this format: YYYY-MM-DD.
public static func date(_ timestring: String, _ modifiers: String...) -> Expression<Date?> {
return timefunction("date", timestring: timestring, modifiers: modifiers)
}
/// The time() function returns the time as HH:MM:SS.
public static func time(_ timestring: String, _ modifiers: String...) -> Expression<Date?> {
return timefunction("time", timestring: timestring, modifiers: modifiers)
}
/// The datetime() function returns "YYYY-MM-DD HH:MM:SS".
public static func datetime(_ timestring: String, _ modifiers: String...) -> Expression<Date?> {
return timefunction("datetime", timestring: timestring, modifiers: modifiers)
}
/// The julianday() function returns the Julian day -
/// the number of days since noon in Greenwich on November 24, 4714 B.C.
public static func julianday(_ timestring: String, _ modifiers: String...) -> Expression<Date?> {
return timefunction("julianday", timestring: timestring, modifiers: modifiers)
}
/// The strftime() routine returns the date formatted according to the format string specified as the first argument.
public static func strftime(_ format: String, _ timestring: String, _ modifiers: String...) -> Expression<Date?> {
if !modifiers.isEmpty {
let templates = [String](repeating: "?", count: modifiers.count).joined(separator: ", ")
return Expression("strftime(?, ?, \(templates))", [format, timestring] + modifiers)
}
return Expression("strftime(?, ?)", [format, timestring])
}
private static func timefunction(_ name: String, timestring: String, modifiers: [String]) -> Expression<Date?> {
if !modifiers.isEmpty {
let templates = [String](repeating: "?", count: modifiers.count).joined(separator: ", ")
return Expression("\(name)(?, \(templates))", [timestring] + modifiers)
}
return Expression("\(name)(?)", [timestring])
}
}
extension Date {
public var date: Expression<Date?> {
return DateFunctions.date(dateFormatter.string(from: self))
}
public var time: Expression<Date?> {
return DateFunctions.time(dateFormatter.string(from: self))
}
public var datetime: Expression<Date?> {
return DateFunctions.datetime(dateFormatter.string(from: self))
}
public var julianday: Expression<Date?> {
return DateFunctions.julianday(dateFormatter.string(from: self))
}
}
extension Expression where UnderlyingType == Date {
public var date: Expression<Date> {
return Expression<Date>("date(\(template))", bindings)
}
public var time: Expression<Date> {
return Expression<Date>("time(\(template))", bindings)
}
public var datetime: Expression<Date> {
return Expression<Date>("datetime(\(template))", bindings)
}
public var julianday: Expression<Date> {
return Expression<Date>("julianday(\(template))", bindings)
}
}

View File

@ -73,7 +73,7 @@ public protocol Expressible {
extension Expressible { extension Expressible {
// naïve compiler for statements that cant be bound, e.g., CREATE TABLE // naïve compiler for statements that cant be bound, e.g., CREATE TABLE
// FIXME: use @testable and make internal // FIXME: make internal (0.12.0)
public func asSQL() -> String { public func asSQL() -> String {
let expressed = expression let expressed = expression
var idx = 0 var idx = 0

View File

@ -474,11 +474,44 @@ public func <=<V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<Bool?> wher
return infix(lhs, rhs) return infix(lhs, rhs)
} }
public func ~=<V : Value>(lhs: ClosedRange<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Binding & Comparable { public func ~=<V : Value>(lhs: ClosedRange<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable & Value {
return Expression("\(rhs.template) BETWEEN ? AND ?", rhs.bindings + [lhs.lowerBound as? Binding, lhs.upperBound as? Binding]) return Expression("\(rhs.template) BETWEEN ? AND ?", rhs.bindings + [lhs.lowerBound.datatypeValue, lhs.upperBound.datatypeValue])
} }
public func ~=<V : Value>(lhs: ClosedRange<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Binding & Comparable {
return Expression("\(rhs.template) BETWEEN ? AND ?", rhs.bindings + [lhs.lowerBound as? Binding, lhs.upperBound as? Binding]) public func ~=<V : Value>(lhs: ClosedRange<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable & Value {
return Expression("\(rhs.template) BETWEEN ? AND ?", rhs.bindings + [lhs.lowerBound.datatypeValue, lhs.upperBound.datatypeValue])
}
public func ~=<V : Value>(lhs: Range<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable & Value {
return Expression("\(rhs.template) >= ? AND \(rhs.template) < ?", rhs.bindings + [lhs.lowerBound.datatypeValue] + rhs.bindings + [lhs.upperBound.datatypeValue])
}
public func ~=<V : Value>(lhs: Range<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable & Value {
return Expression("\(rhs.template) >= ? AND \(rhs.template) < ?", rhs.bindings + [lhs.lowerBound.datatypeValue] + rhs.bindings + [lhs.upperBound.datatypeValue])
}
public func ~=<V : Value>(lhs: PartialRangeThrough<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable & Value {
return Expression("\(rhs.template) <= ?", rhs.bindings + [lhs.upperBound.datatypeValue])
}
public func ~=<V : Value>(lhs: PartialRangeThrough<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable & Value {
return Expression("\(rhs.template) <= ?", rhs.bindings + [lhs.upperBound.datatypeValue])
}
public func ~=<V : Value>(lhs: PartialRangeUpTo<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable & Value {
return Expression("\(rhs.template) < ?", rhs.bindings + [lhs.upperBound.datatypeValue])
}
public func ~=<V : Value>(lhs: PartialRangeUpTo<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable & Value {
return Expression("\(rhs.template) < ?", rhs.bindings + [lhs.upperBound.datatypeValue])
}
public func ~=<V : Value>(lhs: PartialRangeFrom<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable & Value {
return Expression("\(rhs.template) >= ?", rhs.bindings + [lhs.lowerBound.datatypeValue])
}
public func ~=<V : Value>(lhs: PartialRangeFrom<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable & Value {
return Expression("\(rhs.template) >= ?", rhs.bindings + [lhs.lowerBound.datatypeValue])
} }
// MARK: - // MARK: -

View File

@ -22,6 +22,8 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
import Foundation
public protocol QueryType : Expressible { public protocol QueryType : Expressible {
var clauses: QueryClauses { get set } var clauses: QueryClauses { get set }
@ -180,6 +182,27 @@ extension QueryType {
return query return query
} }
// MARK: UNION
/// Adds a `UNION` clause to the query.
///
/// let users = Table("users")
/// let email = Expression<String>("email")
///
/// users.filter(email == "alice@example.com").union(users.filter(email == "sally@example.com"))
/// // SELECT * FROM "users" WHERE email = 'alice@example.com' UNION SELECT * FROM "users" WHERE email = 'sally@example.com'
///
/// - Parameters:
///
/// - table: A query representing the other table.
///
/// - Returns: A query with the given `UNION` clause applied.
public func union(_ table: QueryType) -> Self {
var query = self
query.clauses.union.append(table)
return query
}
// MARK: JOIN // MARK: JOIN
/// Adds a `JOIN` clause to the query. /// Adds a `JOIN` clause to the query.
@ -494,8 +517,9 @@ extension QueryType {
return nil return nil
} }
return " ".join(clauses.join.map { type, query, condition in return " ".join(clauses.join.map { arg in
" ".join([ let (type, query, condition) = arg
return " ".join([
Expression<Void>(literal: "\(type.rawValue) JOIN"), Expression<Void>(literal: "\(type.rawValue) JOIN"),
query.tableName(alias: true), query.tableName(alias: true),
Expression<Void>(literal: "ON"), Expression<Void>(literal: "ON"),
@ -565,6 +589,19 @@ extension QueryType {
Expression<Void>(literal: "OFFSET \(offset)") Expression<Void>(literal: "OFFSET \(offset)")
]) ])
} }
fileprivate var unionClause: Expressible? {
guard !clauses.union.isEmpty else {
return nil
}
return " ".join(clauses.union.map { query in
" ".join([
Expression<Void>(literal: "UNION"),
query
])
})
}
// MARK: - // MARK: -
@ -648,7 +685,9 @@ extension QueryType {
tableName(), tableName(),
Expression<Void>(literal: "SET"), Expression<Void>(literal: "SET"),
", ".join(values.map { " = ".join([$0.column, $0.value]) }), ", ".join(values.map { " = ".join([$0.column, $0.value]) }),
whereClause whereClause,
orderClause,
limitOffsetClause
] ]
return Update(" ".join(clauses.flatMap { $0 }).expression) return Update(" ".join(clauses.flatMap { $0 }).expression)
@ -660,7 +699,9 @@ extension QueryType {
let clauses: [Expressible?] = [ let clauses: [Expressible?] = [
Expression<Void>(literal: "DELETE FROM"), Expression<Void>(literal: "DELETE FROM"),
tableName(), tableName(),
whereClause whereClause,
orderClause,
limitOffsetClause
] ]
return Delete(" ".join(clauses.flatMap { $0 }).expression) return Delete(" ".join(clauses.flatMap { $0 }).expression)
@ -687,47 +728,11 @@ extension QueryType {
return Expression(".".join([tableName(), column]).expression) return Expression(".".join([tableName(), column]).expression)
} }
// FIXME: rdar://problem/18673897 // subscript<T> public subscript<T>(column: Expression<T>) -> Expression<T> {
public subscript(column: Expression<Blob>) -> Expression<Blob> {
return namespace(column)
}
public subscript(column: Expression<Blob?>) -> Expression<Blob?> {
return namespace(column) return namespace(column)
} }
public subscript(column: Expression<Bool>) -> Expression<Bool> { public subscript<T>(column: Expression<T?>) -> Expression<T?> {
return namespace(column)
}
public subscript(column: Expression<Bool?>) -> Expression<Bool?> {
return namespace(column)
}
public subscript(column: Expression<Double>) -> Expression<Double> {
return namespace(column)
}
public subscript(column: Expression<Double?>) -> Expression<Double?> {
return namespace(column)
}
public subscript(column: Expression<Int>) -> Expression<Int> {
return namespace(column)
}
public subscript(column: Expression<Int?>) -> Expression<Int?> {
return namespace(column)
}
public subscript(column: Expression<Int64>) -> Expression<Int64> {
return namespace(column)
}
public subscript(column: Expression<Int64?>) -> Expression<Int64?> {
return namespace(column)
}
public subscript(column: Expression<String>) -> Expression<String> {
return namespace(column)
}
public subscript(column: Expression<String?>) -> Expression<String?> {
return namespace(column) return namespace(column)
} }
@ -779,6 +784,7 @@ extension QueryType {
joinClause, joinClause,
whereClause, whereClause,
groupByClause, groupByClause,
unionClause,
orderClause, orderClause,
limitOffsetClause limitOffsetClause
] ]
@ -890,59 +896,88 @@ public struct Delete : ExpressionType {
} }
public struct RowIterator: FailableIterator {
public typealias Element = Row
let statement: Statement
let columnNames: [String: Int]
public func failableNext() throws -> Row? {
return try statement.failableNext().flatMap { Row(columnNames, $0) }
}
public func map<T>(_ transform: (Element) throws -> T) throws -> [T] {
var elements = [T]()
while let row = try failableNext() {
elements.append(try transform(row))
}
return elements
}
}
extension Connection { extension Connection {
public func prepare(_ query: QueryType) throws -> AnySequence<Row> { public func prepare(_ query: QueryType) throws -> AnySequence<Row> {
let expression = query.expression let expression = query.expression
let statement = try prepare(expression.template, expression.bindings) let statement = try prepare(expression.template, expression.bindings)
let columnNames: [String: Int] = try { let columnNames = try columnNamesForQuery(query)
var (columnNames, idx) = ([String: Int](), 0)
column: for each in query.clauses.select.columns {
var names = each.expression.template.characters.split { $0 == "." }.map(String.init)
let column = names.removeLast()
let namespace = names.joined(separator: ".")
func expandGlob(_ namespace: Bool) -> ((QueryType) throws -> Void) {
return { (query: QueryType) throws -> (Void) in
var q = type(of: query).init(query.clauses.from.name, database: query.clauses.from.database)
q.clauses.select = query.clauses.select
let e = q.expression
var names = try self.prepare(e.template, e.bindings).columnNames.map { $0.quote() }
if namespace { names = names.map { "\(query.tableName().expression.template).\($0)" } }
for name in names { columnNames[name] = idx; idx += 1 }
}
}
if column == "*" {
var select = query
select.clauses.select = (false, [Expression<Void>(literal: "*") as Expressible])
let queries = [select] + query.clauses.join.map { $0.query }
if !namespace.isEmpty {
for q in queries {
if q.tableName().expression.template == namespace {
try expandGlob(true)(q)
continue column
}
}
fatalError("no such table: \(namespace)")
}
for q in queries {
try expandGlob(query.clauses.join.count > 0)(q)
}
continue
}
columnNames[each.expression.template] = idx
idx += 1
}
return columnNames
}()
return AnySequence { return AnySequence {
AnyIterator { statement.next().map { Row(columnNames, $0) } } AnyIterator { statement.next().map { Row(columnNames, $0) } }
} }
} }
public func prepareRowIterator(_ query: QueryType) throws -> RowIterator {
let expression = query.expression
let statement = try prepare(expression.template, expression.bindings)
return RowIterator(statement: statement, columnNames: try columnNamesForQuery(query))
}
private func columnNamesForQuery(_ query: QueryType) throws -> [String: Int] {
var (columnNames, idx) = ([String: Int](), 0)
column: for each in query.clauses.select.columns {
var names = each.expression.template.characters.split { $0 == "." }.map(String.init)
let column = names.removeLast()
let namespace = names.joined(separator: ".")
func expandGlob(_ namespace: Bool) -> ((QueryType) throws -> Void) {
return { (query: QueryType) throws -> (Void) in
var q = type(of: query).init(query.clauses.from.name, database: query.clauses.from.database)
q.clauses.select = query.clauses.select
let e = q.expression
var names = try self.prepare(e.template, e.bindings).columnNames.map { $0.quote() }
if namespace { names = names.map { "\(query.tableName().expression.template).\($0)" } }
for name in names { columnNames[name] = idx; idx += 1 }
}
}
if column == "*" {
var select = query
select.clauses.select = (false, [Expression<Void>(literal: "*") as Expressible])
let queries = [select] + query.clauses.join.map { $0.query }
if !namespace.isEmpty {
for q in queries {
if q.tableName().expression.template == namespace {
try expandGlob(true)(q)
continue column
}
throw QueryError.noSuchTable(name: namespace)
}
throw QueryError.noSuchTable(name: namespace)
}
for q in queries {
try expandGlob(query.clauses.join.count > 0)(q)
}
continue
}
columnNames[each.expression.template] = idx
idx += 1
}
return columnNames
}
public func scalar<V : Value>(_ query: ScalarQuery<V>) throws -> V { public func scalar<V : Value>(_ query: ScalarQuery<V>) throws -> V {
let expression = query.expression let expression = query.expression
@ -967,7 +1002,7 @@ extension Connection {
} }
public func pluck(_ query: QueryType) throws -> Row? { public func pluck(_ query: QueryType) throws -> Row? {
return try prepare(query.limit(1, query.clauses.limit?.offset)).makeIterator().next() return try prepareRowIterator(query.limit(1, query.clauses.limit?.offset)).failableNext()
} }
/// Runs an `Insert` query. /// Runs an `Insert` query.
@ -1023,27 +1058,39 @@ extension Connection {
public struct Row { public struct Row {
fileprivate let columnNames: [String: Int] let columnNames: [String: Int]
fileprivate let values: [Binding?] fileprivate let values: [Binding?]
fileprivate init(_ columnNames: [String: Int], _ values: [Binding?]) { internal init(_ columnNames: [String: Int], _ values: [Binding?]) {
self.columnNames = columnNames self.columnNames = columnNames
self.values = values self.values = values
} }
func hasValue(for column: String) -> Bool {
guard let idx = columnNames[column.quote()] else {
return false
}
return values[idx] != nil
}
/// Returns a rows value for the given column. /// Returns a rows value for the given column.
/// ///
/// - Parameter column: An expression representing a column selected in a Query. /// - Parameter column: An expression representing a column selected in a Query.
/// ///
/// - Returns: The value for the given column. /// - Returns: The value for the given column.
public func get<V: Value>(_ column: Expression<V>) -> V { public func get<V: Value>(_ column: Expression<V>) throws -> V {
return get(Expression<V?>(column))! if let value = try get(Expression<V?>(column)) {
return value
} else {
throw QueryError.unexpectedNullValue(name: column.template)
}
} }
public func get<V: Value>(_ column: Expression<V?>) -> V? {
public func get<V: Value>(_ column: Expression<V?>) throws -> V? {
func valueAtIndex(_ idx: Int) -> V? { func valueAtIndex(_ idx: Int) -> V? {
guard let value = values[idx] as? V.Datatype else { return nil } guard let value = values[idx] as? V.Datatype else { return nil }
return (V.fromDatatypeValue(value) as? V)! return V.fromDatatypeValue(value) as? V
} }
guard let idx = columnNames[column.template] else { guard let idx = columnNames[column.template] else {
@ -1051,61 +1098,24 @@ public struct Row {
switch similar.count { switch similar.count {
case 0: case 0:
fatalError("no such column '\(column.template)' in columns: \(columnNames.keys.sorted())") throw QueryError.noSuchColumn(name: column.template, columns: columnNames.keys.sorted())
case 1: case 1:
return valueAtIndex(columnNames[similar[0]]!) return valueAtIndex(columnNames[similar[0]]!)
default: default:
fatalError("ambiguous column '\(column.template)' (please disambiguate: \(similar))") throw QueryError.ambiguousColumn(name: column.template, similar: similar)
} }
} }
return valueAtIndex(idx) return valueAtIndex(idx)
} }
// FIXME: rdar://problem/18673897 // subscript<T> public subscript<T : Value>(column: Expression<T>) -> T {
return try! get(column)
public subscript(column: Expression<Blob>) -> Blob {
return get(column)
}
public subscript(column: Expression<Blob?>) -> Blob? {
return get(column)
} }
public subscript(column: Expression<Bool>) -> Bool { public subscript<T : Value>(column: Expression<T?>) -> T? {
return get(column) return try! get(column)
} }
public subscript(column: Expression<Bool?>) -> Bool? {
return get(column)
}
public subscript(column: Expression<Double>) -> Double {
return get(column)
}
public subscript(column: Expression<Double?>) -> Double? {
return get(column)
}
public subscript(column: Expression<Int>) -> Int {
return get(column)
}
public subscript(column: Expression<Int?>) -> Int? {
return get(column)
}
public subscript(column: Expression<Int64>) -> Int64 {
return get(column)
}
public subscript(column: Expression<Int64?>) -> Int64? {
return get(column)
}
public subscript(column: Expression<String>) -> String {
return get(column)
}
public subscript(column: Expression<String?>) -> String? {
return get(column)
}
} }
/// Determines the join operator for a querys `JOIN` clause. /// Determines the join operator for a querys `JOIN` clause.
@ -1154,9 +1164,12 @@ public struct QueryClauses {
var order = [Expressible]() var order = [Expressible]()
var limit: (length: Int, offset: Int?)? var limit: (length: Int, offset: Int?)?
var union = [QueryType]()
fileprivate init(_ name: String, alias: String?, database: String?) { fileprivate init(_ name: String, alias: String?, database: String?) {
self.from = (name, alias, database) self.from = (name, alias, database)
} }
} }

View File

@ -42,7 +42,7 @@ extension Table {
block(builder) block(builder)
let clauses: [Expressible?] = [ let clauses: [Expressible?] = [
create(Table.identifier, tableName(), temporary ? .Temporary : nil, ifNotExists), create(Table.identifier, tableName(), temporary ? .temporary : nil, ifNotExists),
"".wrap(builder.definitions) as Expression<Void>, "".wrap(builder.definitions) as Expression<Void>,
withoutRowid ? Expression<Void>(literal: "WITHOUT ROWID") : nil withoutRowid ? Expression<Void>(literal: "WITHOUT ROWID") : nil
] ]
@ -52,7 +52,7 @@ extension Table {
public func create(_ query: QueryType, temporary: Bool = false, ifNotExists: Bool = false) -> String { public func create(_ query: QueryType, temporary: Bool = false, ifNotExists: Bool = false) -> String {
let clauses: [Expressible?] = [ let clauses: [Expressible?] = [
create(Table.identifier, tableName(), temporary ? .Temporary : nil, ifNotExists), create(Table.identifier, tableName(), temporary ? .temporary : nil, ifNotExists),
Expression<Void>(literal: "AS"), Expression<Void>(literal: "AS"),
query query
] ]
@ -127,13 +127,9 @@ extension Table {
// MARK: - CREATE INDEX // MARK: - CREATE INDEX
public func createIndex(_ columns: Expressible...) -> String { public func createIndex(_ columns: Expressible..., unique: Bool = false, ifNotExists: Bool = false) -> String {
return createIndex(columns)
}
public func createIndex(_ columns: [Expressible], unique: Bool = false, ifNotExists: Bool = false) -> String {
let clauses: [Expressible?] = [ let clauses: [Expressible?] = [
create("INDEX", indexName(columns), unique ? .Unique : nil, ifNotExists), create("INDEX", indexName(columns), unique ? .unique : nil, ifNotExists),
Expression<Void>(literal: "ON"), Expression<Void>(literal: "ON"),
tableName(qualified: false), tableName(qualified: false),
"".wrap(columns) as Expression<Void> "".wrap(columns) as Expression<Void>
@ -144,11 +140,8 @@ extension Table {
// MARK: - DROP INDEX // MARK: - DROP INDEX
public func dropIndex(_ columns: Expressible...) -> String {
return dropIndex(columns)
}
public func dropIndex(_ columns: [Expressible], ifExists: Bool = false) -> String { public func dropIndex(_ columns: Expressible..., ifExists: Bool = false) -> String {
return drop("INDEX", indexName(columns), ifExists) return drop("INDEX", indexName(columns), ifExists)
} }
@ -176,7 +169,7 @@ extension View {
public func create(_ query: QueryType, temporary: Bool = false, ifNotExists: Bool = false) -> String { public func create(_ query: QueryType, temporary: Bool = false, ifNotExists: Bool = false) -> String {
let clauses: [Expressible?] = [ let clauses: [Expressible?] = [
create(View.identifier, tableName(), temporary ? .Temporary : nil, ifNotExists), create(View.identifier, tableName(), temporary ? .temporary : nil, ifNotExists),
Expression<Void>(literal: "AS"), Expression<Void>(literal: "AS"),
query query
] ]
@ -307,27 +300,27 @@ public final class TableBuilder {
} }
public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil, defaultValue: Expression<V>? = nil, collate: Collation) where V.Datatype == String { public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil, defaultValue: Expression<V>? = nil, collate: Collation) where V.Datatype == String {
column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, collate) column(name, V.declaredDatatype, nil, true, unique, check, defaultValue, nil, collate)
} }
public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil, defaultValue: Expression<V?>, collate: Collation) where V.Datatype == String { public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil, defaultValue: Expression<V?>, collate: Collation) where V.Datatype == String {
column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, collate) column(name, V.declaredDatatype, nil, true, unique, check, defaultValue, nil, collate)
} }
public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil, defaultValue: V, collate: Collation) where V.Datatype == String { public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil, defaultValue: V, collate: Collation) where V.Datatype == String {
column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, collate) column(name, V.declaredDatatype, nil, true, unique, check, defaultValue, nil, collate)
} }
public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>, defaultValue: Expression<V>? = nil, collate: Collation) where V.Datatype == String { public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>, defaultValue: Expression<V>? = nil, collate: Collation) where V.Datatype == String {
column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, collate) column(name, V.declaredDatatype, nil, true, unique, check, defaultValue, nil, collate)
} }
public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>, defaultValue: Expression<V?>, collate: Collation) where V.Datatype == String { public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>, defaultValue: Expression<V?>, collate: Collation) where V.Datatype == String {
column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, collate) column(name, V.declaredDatatype, nil, true, unique, check, defaultValue, nil, collate)
} }
public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>, defaultValue: V, collate: Collation) where V.Datatype == String { public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>, defaultValue: V, collate: Collation) where V.Datatype == String {
column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, collate) column(name, V.declaredDatatype, nil, true, unique, check, defaultValue, nil, collate)
} }
fileprivate func column(_ name: Expressible, _ datatype: String, _ primaryKey: PrimaryKey?, _ null: Bool, _ unique: Bool, _ check: Expressible?, _ defaultValue: Expressible?, _ references: (QueryType, Expressible)?, _ collate: Collation?) { fileprivate func column(_ name: Expressible, _ datatype: String, _ primaryKey: PrimaryKey?, _ null: Bool, _ unique: Bool, _ check: Expressible?, _ defaultValue: Expressible?, _ references: (QueryType, Expressible)?, _ collate: Collation?) {
@ -513,8 +506,8 @@ private func reference(_ primary: (QueryType, Expressible)) -> Expressible {
private enum Modifier : String { private enum Modifier : String {
case Unique = "UNIQUE" case unique = "UNIQUE"
case Temporary = "TEMPORARY" case temporary = "TEMPORARY"
} }

View File

@ -112,14 +112,14 @@ static const sqlite3_tokenizer_module __SQLiteTokenizerModule = {
__SQLiteTokenizerNext __SQLiteTokenizerNext
}; };
int _SQLiteRegisterTokenizer(SQLiteHandle * db, const char * moduleName, const char * submoduleName, _SQLiteTokenizerNextCallback callback) { int _SQLiteRegisterTokenizer(sqlite3 *db, const char * moduleName, const char * submoduleName, _SQLiteTokenizerNextCallback callback) {
static dispatch_once_t onceToken; static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ dispatch_once(&onceToken, ^{
__SQLiteTokenizerMap = [NSMutableDictionary new]; __SQLiteTokenizerMap = [NSMutableDictionary new];
}); });
sqlite3_stmt * stmt; sqlite3_stmt * stmt;
int status = sqlite3_prepare_v2((sqlite3 *)db, "SELECT fts3_tokenizer(?, ?)", -1, &stmt, 0); int status = sqlite3_prepare_v2(db, "SELECT fts3_tokenizer(?, ?)", -1, &stmt, 0);
if (status != SQLITE_OK ){ if (status != SQLITE_OK ){
return status; return status;
} }

View File

@ -24,14 +24,10 @@
@import Foundation; @import Foundation;
#ifndef COCOAPODS
#import "sqlite3.h" #import "sqlite3.h"
#endif
typedef struct SQLiteHandle SQLiteHandle; // CocoaPods workaround
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
typedef NSString * _Nullable (^_SQLiteTokenizerNextCallback)(const char * input, int * inputOffset, int * inputLength); typedef NSString * _Nullable (^_SQLiteTokenizerNextCallback)(const char *input, int *inputOffset, int *inputLength);
int _SQLiteRegisterTokenizer(SQLiteHandle * db, const char * module, const char * tokenizer, _Nullable _SQLiteTokenizerNextCallback callback); int _SQLiteRegisterTokenizer(sqlite3 *db, const char *module, const char *tokenizer, _Nullable _SQLiteTokenizerNextCallback callback);
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@ -3,11 +3,11 @@ This application makes use of the following third party libraries:
## Crashlytics ## Crashlytics
Fabric: Copyright 2016 Twitter, Inc. All Rights Reserved. Use of this software is subject to the terms and conditions of the Fabric Software and Services Agreement located at https://fabric.io/terms. Crashlytics Kit: Copyright 2016 Crashlytics, Inc. All Rights Reserved. Use of this software is subject to the terms and conditions of the Crashlytics Terms of Service located at http://try.crashlytics.com/terms/terms-of-service.pdf and the Crashlytics Privacy Policy located at http://try.crashlytics.com/terms/privacy-policy.pdf. OSS: http://get.fabric.io/terms/opensource.txt Fabric: Copyright 2017 Google, Inc. All Rights Reserved. Use of this software is subject to the terms and conditions of the Fabric Software and Services Agreement located at https://fabric.io/terms. Crashlytics Kit: Copyright 2017 Crashlytics, Inc. All Rights Reserved. Use of this software is subject to the terms and conditions of the Crashlytics Terms of Service located at http://try.crashlytics.com/terms/terms-of-service.pdf and the Crashlytics Privacy Policy located at http://try.crashlytics.com/terms/privacy-policy.pdf. OSS: http://get.fabric.io/terms/opensource.txt
## Fabric ## Fabric
Fabric: Copyright 2016 Twitter, Inc. All Rights Reserved. Use of this software is subject to the terms and conditions of the Fabric Software and Services Agreement located at https://fabric.io/terms. OSS: http://get.fabric.io/terms/opensource.txt Fabric: Copyright 2017 Google, Inc. All Rights Reserved. Use of this software is subject to the terms and conditions of the Fabric Software and Services Agreement located at https://fabric.io/terms. OSS: http://get.fabric.io/terms/opensource.txt
## FileMD5Hash ## FileMD5Hash

View File

@ -14,7 +14,7 @@
</dict> </dict>
<dict> <dict>
<key>FooterText</key> <key>FooterText</key>
<string>Fabric: Copyright 2016 Twitter, Inc. All Rights Reserved. Use of this software is subject to the terms and conditions of the Fabric Software and Services Agreement located at https://fabric.io/terms. Crashlytics Kit: Copyright 2016 Crashlytics, Inc. All Rights Reserved. Use of this software is subject to the terms and conditions of the Crashlytics Terms of Service located at http://try.crashlytics.com/terms/terms-of-service.pdf and the Crashlytics Privacy Policy located at http://try.crashlytics.com/terms/privacy-policy.pdf. OSS: http://get.fabric.io/terms/opensource.txt</string> <string>Fabric: Copyright 2017 Google, Inc. All Rights Reserved. Use of this software is subject to the terms and conditions of the Fabric Software and Services Agreement located at https://fabric.io/terms. Crashlytics Kit: Copyright 2017 Crashlytics, Inc. All Rights Reserved. Use of this software is subject to the terms and conditions of the Crashlytics Terms of Service located at http://try.crashlytics.com/terms/terms-of-service.pdf and the Crashlytics Privacy Policy located at http://try.crashlytics.com/terms/privacy-policy.pdf. OSS: http://get.fabric.io/terms/opensource.txt</string>
<key>License</key> <key>License</key>
<string>Commercial</string> <string>Commercial</string>
<key>Title</key> <key>Title</key>
@ -24,7 +24,7 @@
</dict> </dict>
<dict> <dict>
<key>FooterText</key> <key>FooterText</key>
<string>Fabric: Copyright 2016 Twitter, Inc. All Rights Reserved. Use of this software is subject to the terms and conditions of the Fabric Software and Services Agreement located at https://fabric.io/terms. OSS: http://get.fabric.io/terms/opensource.txt</string> <string>Fabric: Copyright 2017 Google, Inc. All Rights Reserved. Use of this software is subject to the terms and conditions of the Fabric Software and Services Agreement located at https://fabric.io/terms. OSS: http://get.fabric.io/terms/opensource.txt</string>
<key>License</key> <key>License</key>
<string>Commercial</string> <string>Commercial</string>
<key>Title</key> <key>Title</key>

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>FMWK</string> <string>FMWK</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>0.11.3</string> <string>0.11.4</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>

View File

@ -9,15 +9,4 @@ PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/SQLite.swift PODS_TARGET_SRCROOT = ${PODS_ROOT}/SQLite.swift
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES SKIP_INSTALL = YES
SWIFT_INCLUDE_PATHS[sdk=appletvos*] = $(SRCROOT)/SQLite.swift/CocoaPods/appletvos SWIFT_VERSION = 4.0
SWIFT_INCLUDE_PATHS[sdk=appletvsimulator*] = $(SRCROOT)/SQLite.swift/CocoaPods/appletvsimulator
SWIFT_INCLUDE_PATHS[sdk=iphoneos*] = $(SRCROOT)/SQLite.swift/CocoaPods/iphoneos
SWIFT_INCLUDE_PATHS[sdk=iphoneos10.0] = $(SRCROOT)/SQLite.swift/CocoaPods/iphoneos-10.0
SWIFT_INCLUDE_PATHS[sdk=iphonesimulator*] = $(SRCROOT)/SQLite.swift/CocoaPods/iphonesimulator
SWIFT_INCLUDE_PATHS[sdk=iphonesimulator10.0] = $(SRCROOT)/SQLite.swift/CocoaPods/iphonesimulator-10.0
SWIFT_INCLUDE_PATHS[sdk=macosx*] = $(SRCROOT)/SQLite.swift/CocoaPods/macosx
SWIFT_INCLUDE_PATHS[sdk=macosx10.11] = $(SRCROOT)/SQLite.swift/CocoaPods/macosx-10.11
SWIFT_INCLUDE_PATHS[sdk=macosx10.12] = $(SRCROOT)/SQLite.swift/CocoaPods/macosx-10.12
SWIFT_INCLUDE_PATHS[sdk=watchos*] = $(SRCROOT)/SQLite.swift/CocoaPods/watchos
SWIFT_INCLUDE_PATHS[sdk=watchsimulator*] = $(SRCROOT)/SQLite.swift/CocoaPods/watchsimulator
SWIFT_VERSION = 3.0