Swift3 CoreDataソート実装

ソートについてまとめる
以下の関数を日付でソートしたいとする。

func getData() {
    let context = appDelegate.persistentContainer.viewContext

    do {
        expenses = try context.fetch(Expenses.fetchRequest())
    } catch {
        print("Cannot fetch Expenses")
    }
}

このように記載する。

func getData() {
    let context = appDelegate.persistentContainer.viewContext  

    let fetchRequest = NSFetchRequest<ResultType>(entityName: "Expenses")
    let sort = NSSortDescriptor(key: #keyPath(Expenses.date), ascending: true)
    fetchRequest.sortDescriptors = [sort]
    do {
       expenses = try context.fetch(fetchRequest)
    } catch {
        print("Cannot fetch Expenses")
    }
}

NSFetchRequestがsortDescriptorsというプロパティを持っています。

それをインスタンスにセットするだけです。 ちなみにsortDescriptorsは配列をセットします。
こんな感じです。実装したコード

 fetchRequest.sortDescriptors = [
                NSSortDescriptor(key: "最優先のカラム名" , ascending: true),
                NSSortDescriptor(key: "2番目に優先したいカラム名", ascending: true)]

検索してソートするときに、例えば、男女別でソートして、男子を先に表示する。 その後、生年月日でソートするとかソートには複数の条件が考えられるからです。 sortDescriptorsには、次のようなものをセットできます。

   public init(key: String?, ascending: Bool)
   //キーに文字列、降順、昇順か
   
   public init(key: String?, ascending: Bool, selector: Selector?)

selectorって何?美味しいの?という人のために、、

selectorは、処理先のメソッドを指定するもの。
例えば、

selector: #selector(ViewController.hoge(_:)),

と引数で指定したときは、その呼び出す関数を@objつけて記載する。

@objc func hoge(_ sender:Timer) -> Void {
    print("タイマー実行")
}

こんな感じ。 Objective-Cではポインターだったけど、swiftでは構造体となっているので、このように記載しないといけない。 Objective-Cの文字列とセレクタを変換するみたいです。 あとはコンパイラがやってくれる。

以上です。

git まとめ

gitのまとめ

git config

でメールアドレスなどを登録する

git config -l

で設定ファイルを見れるこれは結構使える。

3つの環境がある。

1-作業環境

2-ステージング環境

3-リポジトリ(保存するところ)

リモート(GitHubサーバー)、ローカル(自分のPC)それぞれにこの3つの環境がある。

これがゴチャゴチャになると理解できなくなる。

git add .

作業環境から、ステージング環境に上げる 最後のドット . が大切
現在のという意味。

git commit -m "コミットメッセージを自分でわかるように書く"

これでリポジトリに上がる。 日付やidが割り当てられ、その時の状態に戻ることが可能。

git log -online

logが 1行で表示される 見やすい

git log -p

gitの変更内容が見れる - - - が以前のもの +++が追加されたもの

git status

ステイタス  現在の状況、場所とか確認できる これは大切

git rm index.html のようにする

ファイル消去はgitのコマンドを利用する gitを利用しないで削除すると、gitで管理できなくなり、エラーが出る。 これもポイント

push pull関係

まずリモートの設定をきちんとしておかないといけない。

git remote -v

リモートの状態を確認

git remote set-url origin {URL.git}

リモートのURLを設定するコマンド urlは最後に .git となっているはず.

git push {ローカルのブランチ名}:{リモートのブランチ名}

pushについてのコマンド

pullしたところにしか、pushできない。だから安全。これ結構大切です。

git pull origin {pullしたいリモートブランチ名}:{ローカルブランチ名}

リモートリポジトリ内の特定のブランチをpullしたいときに利用

Swift MVCモデルの役割の具体的説明(途中なので、読まないこと!)

todoアプリを作る MVCもでるで

モデルの役割

データ構造を表現すること

final class Task {
    var text: String // タスクの内容
    var deadline: Date // 締切
    
    // textとdeadlineを引数にとるイニシャライザメソッド
    init (text: String, deadline: Date) {
        self.text = text
        self.deadline = deadline
    }
    
    // dictionaryを引数にとるイニシャライザメソッド。
    init(from dictionary: [String: Any]) {
        text = dictionary["text"] as! String
        deadline = dictionary["deadline"] as! Date
    }
}

Task内容は、 1-タスク内容 2-締め切

モデル--データの振る舞いやロジックを保持する

final class TaskDataSource: NSObject {
    
    private var tasks = [Task]()
    
    func loadData() {
        let userDefaults = UserDefaults.standard
        let taskDictionaries = userDefaults.object(forKey: "tasks") as? [[String: Any]]
        guard let t = taskDictionaries else { return }
        for dic in t {
            let task = Task(from: dic)
            tasks.append(task)
        }
    }

UserDefaultsクラスの役割は

ユーザーのデフォルトデータベースとのインターフェイス。アプリの起動時にキーと値のペアを永続的に保存します。

standardプロパティは

standardUserDefaultsは、現在のアプリケーションの検索リストを検索するように設定されたNSUserDefaultsのグローバルインスタンスを返します。
ざっくりインスタンスの返すとおぼえておくと良いかも。

   func save(task: Task) {
        tasks.append(task)
        
        var taskDictionaries = [[String: Any]]()
        for t in tasks {
            let taskDictionary: [String: Any] = ["text": t.text,
                                                 "deadline": t.deadline]
            taskDictionaries.append(taskDictionary)
        }
        
        let userDefaults = UserDefaults.standard
        userDefaults.set(taskDictionaries, forKey: "tasks")
        userDefaults.synchronize()
    }

Task を保存するために、Tasksをdataに変換して保存する または辞書型にキャストして配列として保存。 ここでは後者

コントローラ--ModelとViewの仲介を行う

rails NameError: uninitialized constant?? Devise入れたらおかしくなったときの対処法

deviseを入れてからおかしくなったので解決法。
このようなエラーが出る。

NameError: uninitialized constant User

rails c
rails s
もできなくなる??

routesの
devise_for :users
を削除するとサーバーが起動する。
原因は デバイスをインストールしていないのに

こちらがインストールコマンド rails g devise:install

デバイスでuserテーブルを作ってしまったようだ。

rails g devise user

これにより、ルーティングにはdeviseが記載され、おかしな挙動になりrailsコマンドが利用できなくなる。

対処法はこちらにあった

qiita.com

この記事ではより、初心者向けに理由を説明したい。

1-routes.rbにあるdevise_for :usersというコードを削除する

これがあるとルーティングがおかしくなるからか、railsコマンドが利用できない。
上記のNameError: uninitialized constant User のエラーが出る。

2-マイグレーションファイルをロールバックする。

Railsは、DBのバージョン管理がデフォルトで利用できる。それがマイグレーションファイル。 テーブルの操作をするときは、マイグレーションファイルを生成して、実行する。 このマイグレーションファイルのバージョンを持っているので、マイグレーションファイルをそのまま削除すると
厄介なことになる。 それで

rake db:rollback

でロールバックと言って、マイグレーションファイルのバージョンを戻す。 それから、userテーブルを作ったときの、マイグレーションファイルを削除する。

3-usrモデルを削除する

rails d model user

このuserモデルをdeviseをインストールしていないのにdeviseで生成したのがすべての間違いのはじまり。

よってここまで戻ってからdeviseをインストールする。

rails g devise:install

めでたし、めでたし

SWift ??とは

??は中値演算子という

オプショナル型が値を持っていれば、それをラップして代入。

持っていなければ、右辺を代入する。

let hoge:Int?
hoge = 1
let fuga = hoge ?? 3

print(fuga)
=>1

値が入っているので1が出力される

let hoge:Int?
hoge = nil
let fuga = hoge ?? 3

print(fuga)
=>3

値が入っていないので??中値演算子の右辺3が代入されている

実例

   override func mapAgent(_ mapAgent: MsMapAgent, showGoogleMap datPerson: DatPerson) {
//datPerson.jusho があるならラップしてjushoへ代入 そうでなければ""を入れなさい
        let jusho = datPerson.jusho ?? ""
//urlにはjushoを代入
        let url = jusho != "" ?
            URL(string:"comgooglemaps://?q=\(jusho)&center=\(datPerson.latitude),\(datPerson.longitude)&zoom=14&views=traffic")!
            :
            URL(string:"comgooglemaps://?center=\(datPerson.latitude),\(datPerson.longitude)&zoom=14&views=traffic")!
        if (UIApplication.shared.canOpenURL(URL(string:"comgooglemaps://")!)) {
//            UIApplication.shared.openURL(url)
            UIApplication.shared.open(url, options: [:], completionHandler: {(success) in
                if (success){
                    NSLog("success")
                }
            })
        } else {
            MsAlert.confirmOk(self, "Cannot open Google Maps")
        }
    }

Swift3 CoreData 最大値取得 (途中!読まないこと!)

まずimport

import Foundation
import UIKit
import CoreData
  static func maxId() -> Int {
        
        //contextはDBファイルそのもの 入れ物を用意
        let appDelegate = UIApplication.shared.delegate as? AppDelegate
        let context = appDelegate?.persistentContainer.viewContext
        /// ここからが最大値の取得方法
        let expressionUserId = "Maxid"
        /// fetchRequestの生成
        let fetchRequest = NSFetchRequest<NSFetchRequestResult>()

\<NSFetchRequestResult はジェネリクス

型をパラメータとして受け取ることができる。ここでは割愛

このクラス定義は

open class NSFetchRequest<ResultType> : NSPersistentStoreRequest, NSCoding where ResultType : NSFetchRequestResult 

open var entity: NSEntityDescription?

というプロパティを持つ

        /// EntityDescriptionの生成
        //in : context contextの中にあるという     意味
        let entityDescription = NSEntityDescription.entity( forEntityName :"Person" , in : context!)

entityの初期化はこのように記載

それに倣っただけ。

open class func entity(forEntityName entityName: String, in context: NSManagedObjectContext) -> NSEntityDescription?
        
        fetchRequest.entity = entityDescription
        //
        let keyPathExpression = NSExpression(forKeyPath: "id")
        //id を探しますよ、カラムはidを持っていてね
        let expression = NSExpression(forFunction: "max:", arguments: [keyPathExpression])
        let expressionDescription = NSExpressionDescription()
        expressionDescription.name = expressionUserId
        expressionDescription.expression = expression
        expressionDescription.expressionResultType = NSAttributeType.integer64AttributeType
        
        fetchRequest.resultType = NSFetchRequestResultType.dictionaryResultType
        fetchRequest.propertiesToFetch = [expressionDescription]

propertiesToFetchは配列を渡さないといけない

**定義はこうなっている 

open var propertiesToFetch: [Any]?

       //エラーハンドリング
        do{
            let results = try context?.fetch(fetchRequest)
            if let maxId = ((results?.first as AnyObject).value(forKey: expressionUserId)) as? Int {
                print("maxId = \(maxId)")
                return maxId + 1
            } else{
                return 1
            }
        }
        catch let error {
            print(error)
        }
        return -1
    }