Slide 1

Slide 1 text

Swift+RealmͰMacΞϓϦέʔγϣϯ Realm Meetup #5

Slide 2

Slide 2 text

ࣗݾ঺հ ݹྛ ढ़༞ גࣜձࣾυϫϯΰ Github : http://github.com/econa77 Twitter : http://twitter.com/econa77 աڈ࡞ͬͨ΋ͷ REVENTIVE, Inc. Modelor, Inc. ResuPress, Inc. Close Presser Coincheck

Slide 3

Slide 3 text

Agenda ɾClipyͱ͸ ɾRealmಋೖͷܦҢ ɾRealmͷಋೖ ɾTips ɾ໰୊఺

Slide 4

Slide 4 text

ClipmenumΛ஌͍ͬͯ·͔͢ʁ

Slide 5

Slide 5 text

Clipmenuͱ͸ ΫϦοϓϘʔυཤྺ & εχϖοτ ( http://www.clipmenu.com/ ) ςΩετ΍ը૾ͳͲ̔छྨͷΫϦοϓϘʔυܗࣜΛཤྺͱͯ͠ه࿥ՄೳͳMacΞϓϦɻ ه࿥ͨ͠ཤྺ͸ɺγϣʔτΧοτ౳Ͱ͍ͭͰ΋ϖʔετՄೳɻ εχϖοτ΋อଘ͠ϖʔετՄೳ ௕͍Objectice-CͷDelegate΋ҰൃͰϖʔετՄೳɻ ΧελϜΞΫγϣϯΛઃఆͯ͠jsͰ֦ுػೳΛॻ͚Δ ex. ϖʔετ࣌ʹจࣈྻΛ͍͡Δ

Slide 6

Slide 6 text

CPU࢖༻཰͕๫૸ ݪҼ ɾه࿥͍ͯ͠ΔΦϒδΣΫτΛશͯϝϞϦ্ʹอଘ ɾ࠷ऴߋ৽͕2010೥ ɹMac OS X 10.6 Snow Leopard ɾϝχϡʔ͕දࣔ͞ΕΔͨͼʹDBɾϝϞϦʹΞΫη ɹεͯ͠࠶ੜ੒ ໰୊఺

Slide 7

Slide 7 text

ClipMenu 1.0.0a1 released (2014/11/11) ɾΦϒδΣΫτΛDBʹ֨ೲ ɾϑϧϦϑΝΫλ(swift΁) ɾViewΛYosemiteʹ࠷దԽ εχϖοτػೳ͸ະϦϦʔε Clipmenu

Slide 8

Slide 8 text

ɾίϯύΠϧͰ͖ͳ͍ ɾ࢖ͬͯΔϥΠϒϥϦ͕5೥લ͘Β͍ʹ։ൃ͕ͱ·ͬͯΔɻ ClipmenuͷObjective-C൛ͷίʔυ͕OSSͱͯ͠ެ։͞Ε͍ͯ·͢ http://github.com/naotaka/ClipMenu ϥΠϒϥϦͷ࡮৽΍طଘͷ໰୊ղܾͷͨΊswift΁ॻ͖௚͠ Φʔϓϯιʔε

Slide 9

Slide 9 text

http://github.com/Clipy/Clipy http://clipy-app.com

Slide 10

Slide 10 text

ɾཤྺΦϒδΣΫτΛϝϞϦͰ͸ͳ͘ϑΝΠϧͱͯ͠σΟεΫอଘ ɹ → ϑΝΠϧอଘύεΛDBʹ֨ೲ ɾطଘͷػೳʢΞΫγϣϯҎ֎ʣΛશͯ࠶ݱ ɾΠϯετʔϧdmgΛΧελϜ ɾObjective-C͔Β Swift ΁ ɾDBΛCoreData͔Β Realm ΁ มߋ఺

Slide 11

Slide 11 text

DEMO

Slide 12

Slide 12 text

ɾઃఆը໘΍εχϖοτฤूը໘ͷ׬શʹมߋ ɾΞΫγϣϯΛ෦෼తʹ෮׆ ɹˠjsͰॻ͘ͱ͔͸ࠓޙ΋ߟ͍͑ͯͳ͍ ݱঢ়͸҆ఆ൛ͱ͸͍͑ɺྼԽ൛Ͱ͔͠ͳ͍ εχϖοτΛ࣠ʹ։ൃ͍ͯ͘͠ ɾεχϖοτϚʔέοτʢԾʣͷ࣮ݱ ɹˠεχϖοτΛࣗ༝ʹΞοϓϩʔυɾμ΢ϯϩʔυͰ͖ ɹɹΔύοέʔδϚωδϟʔͷΑ͏ͳͳʹ͔Λ࡞Δ ͜Ε͔Β

Slide 13

Slide 13 text

Realm DB

Slide 14

Slide 14 text

RealmಋೖܦҢ ݱࡏɺ৽نΞϓϦ։ൃͰRealmಋೖΛݕ౼͍ͯ͠Δ͕ɺຊ൪౤ೖʹ͍͖ͳΓಋ ೖͱ͍͏Θ͚ʹ͸ߦ͔ͳ͍ ͦ͜Ͱɺຊ൪౤ೖͷ࣮ݧతͳҙຯΛࠐΊͯࠓճClipyʹ͸RealmΛಋೖ͍ͯ͠· ͢ɻ ࣾ಺Github͓͡͞ΜͳΒ͵Realm͓͡͞ΜΛ໨ࢦ͍ͨ͠

Slide 15

Slide 15 text

࠷ऴతʹ͸࣮ࡍʹϦϦʔε͢ΔiOSΞϓϦ΁ͷಋೖΛ໨ࢦ͍ͯ͠ΔͨΊɺiOS7 ରԠ͸·ͩඞཁͳͷͰɺObjective-C൛Λ࢖༻ ClipyͷରԠOS͸Mavericks͔ΒͳͷͰɺRealmSwiftΛ࢖༻Ͱ͖Δɻ ࢖༻όʔδϣϯ iOSͰRealmSwiftΛར༻͢Δʹ͸iOS8Ҏ্͕ඞਢ ϓϩδΣΫτ΁ͷΠϯετʔϧ͸CocoaPodsΛ࢖༻ Swift͔ΒRealmObjective-C൛Λ࢖༻͢Δʹ͸ผ్RLMSupportΛΠϯϙʔτ

Slide 16

Slide 16 text

MacΞϓϦ͔ͩΒͱ͍ͬͯಛஈόά͸ͳ͍ ɹˠ جຊతʹ͸iOSͱಉ͡ Ή͠Ζ५୔ʹϝϞϦ͕͋ΔMacͷ΄͏͕RealmͷྗΛ׆͔͢͜ͱ͕Ͱ͖͍ͯΔ ࣗ෼ͱͯ͠͸ࠓޙMacΞϓϦΛ࡞Δͱͨ͠ΒRealmҎ֎͸ͳ͍ Realm + CocoaApplication

Slide 17

Slide 17 text

Tips

Slide 18

Slide 18 text

RLMResult RLMResult͸ৗʹ࠷৽ͷঢ়ଶ͕อͨΕ͍ͯΔ FetchedResultsControllerͱಉ͡Α͏ͳ࢖͍ํ͕Ͱ͖ΔͷͰɺTableviewͷ DataSourceͳͲʹ࢖༻͢Δͱඇৗʹศར ɹˠ σʔλ͕มߋ͞Εͯ΋reloadData()ΛݺͿ͚ͩͰߋ৽͕͔͔Δ private var clips: RLMResults? self.clips = CPYClip.allObjects().sortedResultsUsingProperty("updateTime", ascending: false) self.reloadData() let clips = CPYClip.allObjects().sortedResultsUsingProperty("updateTime", ascending: false)

Slide 19

Slide 19 text

RLMArray͸಺෦Ͱॱ൪͕อূ͞Ε͍ͯΔ ॱ൪ͷೖΕସ͑Λߦ͍͍ͨ RLMArray - (void)moveObjectAtIndex:(NSUInteger)sourceIndex toIndex:(NSUInteger)destinationIndex; PullRequestΛݕࡧͯͨ͠Βࡢ೔ #2273 Ͱ࡞੒͞Ε͍ͯͨ dynamic var snippets = RLMArray(objectClassName: CPYSnippet.className())

Slide 20

Slide 20 text

ΦϒδΣΫτʹมߋ͕͋ͬͨ৔߹ɺ௨஌Λड͚औΔ͜ͱ͕Ͱ͖Δɻ ͲͷΦϒδΣΫτ͕ߋ৽͕͔͔͔ͬͨ෼͔Βͳ͍ɻ ࠓճɺݱࡏ࣮૷தͷΞϓϦͰ͸རศੑ͕ͳ͍ͷͰɺ࢖༻͍ͯ͠ͳ͍ɻ Notification RLMRealm.defaultRealm().addNotificationBlock{ notification, realm in } ݱࡏɺIssueͰ্͕͍ͬͯΔͷͰߋ৽ΦϒδΣΫτ͕औΕΔΑ͏ʹͳΕ͹࢖༻͢Δ༧ఆɻ

Slide 21

Slide 21 text

RealmͷΦϒδΣΫτݕࡧ͸̎छྨ͋ΔʢSwift͔ΒObjC൛Λ࢖༻͢Δ৔߹ͷΈʣ objectsWhereͷ಺෦࣮૷ ฦΓ஋ͷRLMResults͕Φϓγϣφϧͳͷ͔ɺͦ͏Ͱͳ͍ͷ͔ͷҧ͍ ݕࡧ objectsWithPredicate:(NSPredicate *)predicate objectsWhere(predicateFormat: String, _ args: CVarArgType...) public class func objectsWhere(predicateFormat: String, _ args: CVarArgType...) -> RLMResults { return objectsWithPredicate(NSPredicate(format: predicateFormat, arguments: getVaList(args))) } objectsWithPredicate:(NSPredicate *)predicate ɹɹɹɹɹɹɹɹɹ -> RLMResult? objectsWhere(predicateFormat: String, _ args: CVarArgType…) ɹ-> RLMResult

Slide 22

Slide 22 text

ϦϨʔγϣφϧ class CPYFolder: RLMObject { // MARK: - Properties dynamic var index = 0 dynamic var enable = true dynamic var title = "" dynamic var snippets = RLMArray(objectClassName: CPYSnippet.className()) } class CPYSnippet: RLMObject { // MARK: - Properties dynamic var index = 0 dynamic var enable = true dynamic var title = "" dynamic var content = "" dynamic var folder: CPYFolder? { return linkingObjectsOfClass("CPYFolder", forProperty: "snippets").first as? CPYFolder } }

Slide 23

Slide 23 text

RLMResults͕ৗʹ࠷৽ͷ஋Λ͍࣋ͬͯΔͨΊforͰ࡟আ΍ߋ৽Λߦ͏ͱॱ൪͕ͣ ΕΔ ࡟আɾߋ৽ internal func removeFolders() { let realm = RLMRealm.defaultRealm() let folders = CPYFolder.allObjects() for folder in folders { realm.transactionWithBlock({ () -> Void in realm.deleteObjects((folder as! CPYFolder).snippets) realm.deleteObject(folder) }) } }

Slide 24

Slide 24 text

RLMResults͕ৗʹ࠷৽ͷ஋Λ͍࣋ͬͯΔͨΊforͰ࡟আ΍ߋ৽Λߦ͏ͱॱ൪͕ͣ ΕΔ ࡟আɾߋ৽ internal func removeFolders() { let realm = RLMRealm.defaultRealm() let folders = CPYFolder.allObjects() for folder in folders { realm.transactionWithBlock({ () -> Void in realm.deleteObjects((folder as! CPYFolder).snippets) }) } realm.transactionWithBlock { () -> Void in realm.deleteObjects(folders) } }

Slide 25

Slide 25 text

RealmͰͰ͖Δ͚ͩ៉ྷͳEnum Enum class CPYClip: RLMObject { // MARK: - Properties enum DataStatus: String { case Status1 = "status1" case Status2 = "status2" } dynamic var localStatus = DataStatus.Status1.rawValue internal var status: DataStatus { get { return DataStatus(rawValue: self.localStatus) ?? .Status1 } set { self.localStatus = newValue.rawValue } } }

Slide 26

Slide 26 text

DBϑΝΠϧͷ҉߸ԽʹରԠ͍ͯ͠Δ iOS৔߹Ͱ΋DBϑΝΠϧ͕Ӿཡ͞ΕΔ৔߹͸͋ΔͷͰɺϢʔβͷ৘ใͳͲɺݟΒ Εͯ͸͍͚ͳ͍σʔλΛอଘ͢Δ৔߹͸҉߸ԽΛߦ͏ ҉߸Խ MacΞϓϦ͸୭Ͱ΋ӾཡՄೳͳҐஔʹDBϑΝΠϧ͕ஔ͔ΕΔͷͰͰ͖Ε͹҉߸Խ ͓ͯ͘͠ NSMutableData *key = [NSMutableData dataWithLength:64]; SecRandomCopyBytes(kSecRandomDefault, key.length, (uint8_t *)key.mutableBytes); [RLMRealm setEncryptionKey:key forRealmsAtPath:RLMRealm.defaultRealmPath]; RLMResults *dogs = [[Dog objectsWhere:@"name contains 'Fido'"]];

Slide 27

Slide 27 text

໰୊఺

Slide 28

Slide 28 text

໰୊఺ ɾNSOutlineView ɾCustom Initializer ɾNSDataͷnilαϙʔτ ɾNSDate ɾDeleteRule

Slide 29

Slide 29 text

NSOutlineViewͷDataSourceͱͯ͠ RealmObjectΛ࢖͑ͳ͍ NSOutlineView(ݕূத) func outlineView(outlineView: NSOutlineView, numberOfChildrenOfItem item: AnyObject?) -> Int { if item == nil { return Int(self.folders!.count) } else if let folder = item as? CPYFolder { return Int(folder.snippets.count) } return 0 } NSOutlineViewॳΊͯ࢖͏ͷͰ࢖͍ํ͕ؒҧͬͯ ͍Δ͚͔ͩ΋͠Ε·ͤΜ

Slide 30

Slide 30 text

ΦϒδΣΫτʹΧελϜInitializerͷఆٛ Custom Initializer override init() { super.init() } init(title: String) { self.title = title super.init() } // MARK: Primary Key override class func primaryKey() -> String { return "dataHash" } CPYClip(forPrimaryKey: "") forPrimaryKeyϝιου͕ݺ΂ͳ͍

Slide 31

Slide 31 text

αϜωΠϧը૾Λαϙʔτ͢ΔͨΊʹը૾ΛDBʹೖΕ͔ͨͬͨ nilαϙʔτ͕β൛Ͱެ։த ࠓޙͷclipyͷόʔδϣϯΞοϓ Ͱద༻༧ఆ NSDataͷnilαϙʔτ ͦͷଞϓϩδΣΫτʹؔͯ͠͸ɺ ը૾ΛσΟεΫʹอଘͯ͠ύεͷ ΈΛೖΕΔํࣜ

Slide 32

Slide 32 text

NSDate͸ϛϦඵؙ͕ΊΒΕΔ αʔόଆͱͷ௨৴΋ߟ͑Δͱ࢓༷Λ߹Θ͓͖͍ͤͯͨ NSDate let unixTime = NSDate().timeIntervalSince1970 let clip = CPYClip() clip.dataPath = path clip.title = title clip.dataHash = String(hash) clip.updateTime = unixTime clip.primaryType = clipData.primaryType ?? "" RealmͰ࣌ؒΛѻ͏ͱ͖͸શͯUNIXTimeͰѻ͍౎౓ม׵

Slide 33

Slide 33 text

DeleteRule͕ઃఆͰ͖ͳ͍ͨΊɺ਌Λ࡟আ͢Δ࣌͸ɺࣗ෼Ͱ࡟আ͢Δɻ Delete Rule internal func removeFolders() { let realm = RLMRealm.defaultRealm() var folders = CPYFolder.allObjects() for folder in folders { realm.transactionWithBlock({ () -> Void in realm.deleteObjects((folder as! CPYFolder).snippets) }) } realm.transactionWithBlock { () -> Void in realm.deleteObjects(folders) } }

Slide 34

Slide 34 text

·ͱΊ ɾClipy͸͜Ε͔Β΋ศརͳMacΞϓϦέʔγϣϯΛ௥ٻ͍͖͍ͯͨ͠ͷͰɺͲ ɹͳͨͰ΋ؾ͍ͮͨ఺΍όά͕͋Ε͹Issue΍PullRequestΛૹ͍ͬͯͩ͘͞ ɾֶशίετ΋ͦͷଞDBʹൺ΂Δͱ͔ͳΓ௿͘ɺ։ൃ΋׆ൃతͳͷͰͲΜͲ ɹΜಋೖ͍͖͍ͯͨ͠ ɾVersion1.0ʹ౸ୡ͍ͯ͠ͳ͍ͷͰɺૣ҆͘ఆ൛ͱͯ͠ϦϦʔεͯ͠΋Β͑͏ ɹͱخ͍͠