「アプリ開発」カテゴリーアーカイブ

ViewControllerを別のViewControllerの任意ビューにはめ込む処理

public extension UIView {

    public func fillSuperviewWithConstaints() {

        let constraints = NSLayoutConstraint.constraints(fillingSuperviewOf: self)

        if #available(iOS 8, *) {
            NSLayoutConstraint.activate(constraints)
        } else {
            if let superview = superview {
                superview.addConstraints(constraints)
            }
        }
    }

}

public extension NSLayoutConstraint {

    public static func constraints(fillingSuperviewOf view: UIView) -> [NSLayoutConstraint] {
        guard let superview = view.superview else {
            return []
        }

        return [NSLayoutConstraint(item: view, attribute: .top, relatedBy: .equal, toItem: superview, attribute: .top, multiplier: 1.0, constant: 0.0),
                NSLayoutConstraint(item: view, attribute: .bottom, relatedBy: .equal, toItem: superview, attribute: .bottom, multiplier: 1.0, constant: 0.0),
                NSLayoutConstraint(item: view, attribute: .leading, relatedBy: .equal, toItem: superview, attribute: .leading, multiplier: 1.0, constant: 0.0),
                NSLayoutConstraint(item: view, attribute: .trailing, relatedBy: .equal, toItem: superview, attribute: .trailing, multiplier: 1.0, constant: 0.0),
        ]
    }

}

public extension UIViewController {

    public func addChildViewController(_ childViewController: UIViewController, filling parentView: UIView, withContraints: Bool) {

        addChildViewController(childViewController)
        childViewController.didMove(toParentViewController: self)

        if let subView = childViewController.view {
            parentView.addSubview(subView)

            if withContraints {
                subView.translatesAutoresizingMaskIntoConstraints = false
                subView.fillSuperviewWithConstaints()
            } else {
                subView.frame = parentView.bounds
            }
        }
    }

}

Protocol and Value Oriented Programming in UIKit Apps

WWDC2016で、Swiftのstruct、protocol、enumの有効な使い方について紹介するセッションがあった。

Protocol and Value Oriented Programming in UIKit Apps (WWDC 2016 – Session 419)

そのセッションで使われたサンプルコードがこちら。

LucidDreams: Protocol and Value Oriented Programming Sample Code

とても興味深くて、勉強になると思ったが、まだマスターできていない。

ゼロから Core Data を学ぶ

とりあえず読み書きができるまで

プロジェクト作成

Single View Application を作成。
ゼロから作成するので Use Core Data はチェックしない。

Data Model 追加

TestData と名前をつけてプロジェクトに追加。
TestData.xcdatamodeld/TestData.xcdatamodel が作成される。

テストモデルでデータの読み書き

TestModel という Entity を追加し、属性 text: String を追加。

ViewController にボタンを設置し、そのボタンを押すたびにモデルが増えるコードを書く。

import UIKit
import CoreData

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // データをロード
        print("load begin")
        container = NSPersistentContainer(name: "TestData")
        container.loadPersistentStores { (desc, error) in
            if let error = error {
                print("load error: \(error)")
            } else {
                print("load ok: \(desc)")
            }
        }
    }

    @IBAction func testButton(_ sender: Any) {

        let context = container.viewContext

        // データ追加、textに任意文字列設定
        let testModel = NSEntityDescription.insertNewObject(forEntityName: "TestModel",
                                                            into: context)
        testModel.setValue(UUID().uuidString, forKey: "text")

        // 保存
        try! context.save()

        // 列挙
        let request = NSFetchRequest<NSManagedObject>(entityName: "TestModel")
        let models = try! context.fetch(request)
        print("-- model count: \(models.count)")
        for model in models {
            let text = model.value(forKey: "text")!
            print("text: \(text)")
        }
    }

    var container: NSPersistentContainer!
}

ボタンを3回押したときの出力:

load begin
load ok: <NSPersistentStoreDescription: 0x60800004d9b0> (type: SQLite, url: file:///Users/rsahara/Library/Developer/CoreSimulator/Devices/D9D49483-571C-409D-87F2-88556CB3D351/data/Containers/Data/Application/C53CF83C-77F4-4719-BA87-242D158B3DC7/Library/Application%20Support/TestData.sqlite)
-- model count: 1
text: E4AA836E-3F59-4F96-B6EF-CB79EFFA57BB
-- model count: 2
text: E4AA836E-3F59-4F96-B6EF-CB79EFFA57BB
text: 3A64EDFE-D6B2-4C9D-A3AD-4D53DFAD4FFE
-- model count: 3
text: E4AA836E-3F59-4F96-B6EF-CB79EFFA57BB
text: 3A64EDFE-D6B2-4C9D-A3AD-4D53DFAD4FFE
text: F0690E43-8E0A-46B0-A491-482E3E407585

アプリを再起動しても、データは保存されている:

load begin
load ok: <NSPersistentStoreDescription: 0x600000241ce0> (type: SQLite, url: file:///Users/rsahara/Library/Developer/CoreSimulator/Devices/D9D49483-571C-409D-87F2-88556CB3D351/data/Containers/Data/Application/6DE5C1D8-FB7C-48F2-9A38-B2BDF0A8885C/Library/Application%20Support/TestData.sqlite)
-- model count: 4
text: E4AA836E-3F59-4F96-B6EF-CB79EFFA57BB
text: 3A64EDFE-D6B2-4C9D-A3AD-4D53DFAD4FFE
text: F0690E43-8E0A-46B0-A491-482E3E407585
text: F1E3B4FB-9BB9-49CA-B886-C4589A698A5F

iOSアプリデータの保存

iOSアプリの開発を初めて以来、データを保存する際に考察したこと、改善したことが多々あるので、まとめて記事をQiitaにて書いた。

iOSアプリデータの保存 – Qiita

この状態に至るまで、開発中で出合った諸問題:

  • ストレージのスペースが足りなくてハングする問題。
  • Realmファイルがメモリに乗らない問題。
  • Realmファイルのマイグレーションで失敗して起動できない問題。
  • 使用しない無駄なキャッシュを蓄積してしまう問題。
  • キャッシュが増え続ける問題。

Swift でシングルトン

単純なシングルトン

class Feature {
    static let instance = Feature()
}
  • 最初のアクセスのときにインスタンスが作られる。
  • スレッドセーフである。

明示的に初期化・開放するシングルトン

class Feature {
    static var instance: Feature {
        return _instance
    }

    static func initializeInstance() {
        _instance = Feature()
    }

    static func finalizeInstance() {
        _instance = nil
    }

    private static var _instance: Feature! = nil
}
  • スレッドセーフではない。でも明示的にインスタンスするならそこは使う側が注意している前提。

その他の考慮

  • init を private にすれば、外部でのインスタンス作成を防げる。でも使う側はそんなバカではないはずだし、仮に間違った使い方をされたら議論したいのでオープンにしておきたい。
  • Objective-C でも使うなら public クラスにして NSObject を派生し、 init を override。