今回はMacアプリでDBを使いたくてRealmについて調べてみました。
以前に作成したプロジェクトに組み込んでみます。
完成イメージ
環境
- macOS Mojave:10.14.6
- Xcode:10.3
- CocoaPods:1.7.5
Realm インストール
Podfile の作成
$ pod init
$ ls
NSTableViewSample NSTableViewSample.xcodeproj Podfile
Realm インストール
Podfile に pod 'RealmSwift'
を追加
$ vi Podfile
# Uncomment the next line to define a global platform for your project
# platform :osx, '10.14'
target 'NSTableViewSample' do
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
use_frameworks!
# Pods for NSTableViewSample
pod 'RealmSwift'
end
Realm インストール
$ pod install
Analyzing dependencies
Downloading dependencies
Installing Realm (3.17.3)
Installing RealmSwift (3.17.3)
Generating Pods project
Integrating client project
Sending stats
Pod installation complete! There is 1 dependency from the Podfile and 2 total pods installed.
[!] Automatically assigning platform `osx` with version `10.14` on target `NSTableViewSample` because no platform was specified. Please specify a platform for this target in your Podfile. See `https://guides.cocoapods.org/syntax/podfile.html#platform`.
Realm を使う
pod install
で作成された workspace を開く
$ open NSTableViewSample.xcworkspace
株式情報保存用のクラス(StockInfo)を作成
Object クラスを継承して作成します。
StockInfo.swift
import Cocoa
import RealmSwift
class StockInfo: Object {
@objc dynamic var name = ""
@objc dynamic var price = -1
@objc dynamic var status = StockStatus.flat.rawValue
@objc dynamic var createdAt = Date()
}
extension StockInfo {
/// 状態をEnum形式で返却
var stockStatus: StockStatus {
get {
return StockStatus(rawValue: self.status) ?? StockStatus.flat
}
set {
self.status = newValue.rawValue
}
}
/// StockInfo オブジェクトを作成
class func create(realm: Realm, name: String, price: Int, status: StockStatus) -> StockInfo {
let stockInfo = realm.create(StockInfo.self)
stockInfo.name = name
stockInfo.price = price
stockInfo.stockStatus = status
return stockInfo
}
/// 企業名で部分一致検索(空の場合は全件取得)
class func objects(searchValue: String = "") -> Results<StockInfo> {
var result = try! Realm().objects(StockInfo.self)
let searchValue = searchValue.trimmingCharacters(in: .whitespaces)
if searchValue != "" {
result = result.filter("name CONTAINS %@", searchValue)
}
return result.sorted(byKeyPath: "createdAt")
}
/// デバッグ用
class func debugInitData() {
let realm = try! Realm()
try! realm.write {
// データクリア
realm.deleteAll()
// サンプルデータ作成
let _ = StockInfo.create(realm: realm, name: "○○株式会社", price: 1000, status: .up)
let _ = StockInfo.create(realm: realm, name: "○×水産", price: 12345, status: .down)
let _ = StockInfo.create(realm: realm, name: "株式会社□○", price: 345, status: .flat)
let _ = StockInfo.create(realm: realm, name: "×△ホールディングス", price: 321, status: .up)
let _ = StockInfo.create(realm: realm, name: "ABC BANK", price: 20, status: .down)
let _ = StockInfo.create(realm: realm, name: "▼○重工", price: 98000, status: .up)
// サンプル用に1,000 件データを作成
for i in 1...1_000 {
let status = StockStatus.init(rawValue: Int.random(in: 0..<3))!
let price = Int.random(in: -1..<10_000_000)
let _ = StockInfo.create(realm: realm, name: "○△×株式会社 \(i)", price: price, status: status)
}
}
}
}
株価情報の状態を表すEnum(StockStatus)を作成
StockStatus.swift
import Cocoa
enum StockStatus: Int {
case flat = 0
case up
case down
/// 状態ごとのImageオブジェクト
var image: NSImage {
get {
switch self {
case .flat:
return NSImage(named: NSImage.Name("flat"))!
case .up:
return NSImage(named: NSImage.Name("up"))!
case .down:
return NSImage(named: NSImage.Name("down"))!
}
}
}
/// 状態ごとのカラーオブジェクト
var color: NSColor {
get {
switch self {
case .flat:
return .gray
case .up:
return .green
case .down:
return .red
}
}
}
}
StockInfo クラスを利用して株価情報を表示
細かい説明は省きますが、リファクタリングして多少見やすくなっているはずです。
ViewController.swift
import Cocoa
class ViewController: NSViewController {
// MARK: - IBOutlet
@IBOutlet private weak var tableView: NSTableView! {
didSet {
self.tableView.dataSource = self
self.tableView.delegate = self
self.tableView.headerView = nil
self.tableView.rowHeight = 88
}
}
@IBOutlet private weak var countTextField: NSTextField!
// MARK: - private variables
private var stockValues = StockInfo.objects()
// MARK: - override func
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// デバッグ用にデータ作成
StockInfo.debugInitData()
// 株価情報をViewに反映
self.updateStockValuesCount()
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
// MARK: - private func
private func updateStockValuesCount() {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
let commaString = formatter.string(from: NSNumber(value: self.stockValues.count))!
self.countTextField.stringValue = "\(commaString)件"
}
// MARK: - actions
@IBAction func actionSearchField(_ sender: NSSearchField) {
self.stockValues = StockInfo.objects(searchValue: sender.stringValue)
self.tableView.reloadData()
self.updateStockValuesCount()
}
}
extension ViewController: NSTableViewDataSource, NSTableViewDelegate {
// MARK: - NSTableViewDataSource
func numberOfRows(in tableView: NSTableView) -> Int {
return self.stockValues.count
}
// MARK: - NSTableViewDelegate
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let result = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "MyView"), owner: self) as! CustomNSTableCellView
// Set the stringValue of the cell's text field to the nameArray value at row
let stock = self.stockValues[row]
result.image = stock.stockStatus.image
result.name = stock.name
result.color = stock.stockStatus.color
result.price = stock.price
// Return the result
return result
}
}