Slide 1

Slide 1 text

CZ,ZPNF J04%$+BQBO 'JOEFS4ZOD&YUFOTJPOͰ .BD޲͚ศརπʔϧΛ࡞Ζ͏

Slide 2

Slide 2 text

,ZPNFʢ͖ΐΊʣ ͓࢓ࣄɿ$ZCP[VϞόΠϧΤϯδχΞ 4XJGUྺɿ೥ ಘҙ෼໺ɿNBD04޲͚ৗறܕΞϓϦ LZPNF ,ZPNF ,ZPNFTVLF

Slide 3

Slide 3 text

'JOEFS4ZOD&YUFOTJPOͱ͸

Slide 4

Slide 4 text

'JOEFS4ZOD&YUFOTJPO ϩʔΧϧͱϦϞʔτͰϑΝΠϧΛಉظ͢ΔΞϓϦͷͨΊʹ༻ҙ͞Εͨػೳ֦ு ‣ ྫɿ%SPQ#PY΍(PPHMF%SJWFͷΑ͏ͳΦϯϥΠϯετϨʔδαʔϏε

Slide 5

Slide 5 text

'JOEFS4ZOD&YUFOTJPO ࢦఆͨ͠ϑΥϧμͱͦͷ֊૚ԼͷΞΠςϜͷঢ়ଶΛ؂ࢹͰ͖Δ ‣ ϑΝΠϧͷछྨɺ࡞੒೔ɺมߋ೔ͳͲͷ৘ใ؂ࢹ ‎ ಉظର৅͔Ͳ͏͔ʗಉظࡁΈ͔Ͳ͏͔ͷ൑அ ‣ ࢦఆͨ͠ϑΥϧμΛݱࡏϢʔβ͕ૢ࡞͍ͯ͠Δ͔ͷ؂ࢹ ‎ ಉظॲཧͷ࠷దԽ

Slide 6

Slide 6 text

'JOEFS4ZOD&YUFOTJPO 'JOEFS্ͰΞΠςϜʹόοδΛ෇༩ͯ͠ɺΞΠςϜͷಉظঢ়ଶΛఏࣔͰ͖Δ όοδ

Slide 7

Slide 7 text

'JOEFS4ZOD&YUFOTJPO ϑΝΠϧ΍ϑΥϧμͷ؅ཧλεΫΛ࣮ߦ͢ΔɺΧελϜίϯςΩετϝχϡʔ ΛఏࣔͰ͖Δ ΧελϜίϯςΩετϝχϡʔͱ͸ʁ σεΫτοϓ΍'JOEFS্ͰɺӈΫϦοΫ΍ $POUSPMʴΫϦοΫΛ͢Δͱग़Δϝχϡʔ

Slide 8

Slide 8 text

'JOEFS4ZOD&YUFOTJPO શϑΝΠϧͷಉظͳͲɺάϩʔόϧͳλεΫΛ࣮ߦ͢ΔΧελϜπʔϧόʔ ϘλϯΛ'JOEFSͷπʔϧόʔʹ௥ՃͰ͖Δ 'JOEFS ΧελϜπʔϧόʔϘλϯ

Slide 9

Slide 9 text

'JOEFS4ZOD&YUFOTJPO ͍͍ͩͨ෼͔ͬͨͧʂ

Slide 10

Slide 10 text

Ͱ΋͜ͷࢠɺ

Slide 11

Slide 11 text

ϑΝΠϧΛಉظ͢ΔΞϓϦҎ֎ʹ΋ ࢓૊ΈΛྲྀ༻Ͱ͖ͦ͏ʜ

Slide 12

Slide 12 text

ࢲͷղऍ

Slide 13

Slide 13 text

ࢲͷղऍ Ϣʔβ͕ಛఆͷϑΥϧμΛૢ࡞࢝͠Ίͨ͜ͱΛݕ஌Ͱ͖Δ ಉظͱؔ܎ͳ͘ɺϑΝΠϧ΍ϑΥϧμͷঢ়ଶʹैͬͯόοδΛ෇༩Ͱ͖Δ 'JOEFS্Ͱબ୒தͷϑΝΠϧ΍ϑΥϧμʹରͯ͠೚ҙͷλεΫΛ࣮ߦͰ͖Δ ΋͸΍ɺબ୒தͷϑΝΠϧ΍ϑΥϧμͱؔ܎ͳ͘ɺ೚ҙͷϧʔνϯλεΫΛ ࣗಈԽͨ͠ίϚϯυΛୟ͚Δ ‎ ϝχϡʔόʔৗறܕͱ͸ҟͳΔܗͷ ɹɹɹɹɹɹৗறܕϢʔςΟϦςΟπʔϧͷ࡞੒ʹ࢖͑Δ!

Slide 14

Slide 14 text

'JOEFS4ZOD&YUFOTJPO࣮૷ͷجຊ

Slide 15

Slide 15 text

'JOEFS4ZOD&YUFOTJPO࣮૷ͷجຊʢجຊͷΩฤʣ ऩ༰ΞϓϦͱ'JOEFS4ZOD&YUFOTJPOͷ̎ͭͷλʔήοτͰߏ੒͞ΕΔ ػೳ֦ுͷऩ༰ΞϓϦ 'JOEFS4ZOD&YUFOTJPO ʢʹ'JOEFSػೳ֦ுʣ

Slide 16

Slide 16 text

'JOEFSػೳ֦ு͸ɺΞϓϦͷΠϯετʔϧޙɺγεςϜ؀ڥઃఆ͔Β ༗ޮʹ͢Δ͜ͱͰػೳ͢Δ γεςϜ؀ڥઃఆ 'JOEFS4ZOD&YUFOTJPO࣮૷ͷجຊʢجຊͷΩฤʣ

Slide 17

Slide 17 text

‎ ऩ༰ΞϓϦ͸ɺػೳ֦ுͷ༗ޮΛଅ͢ಋઢͷ໾ׂΛ୲͏ ಋઢͷྫ 'JOEFS4ZOD&YUFOTJPO࣮૷ͷجຊʢجຊͷΩฤʣ

Slide 18

Slide 18 text

'JOEFS4ZOD&YUFOTJPO࣮૷ͷجຊʢԼ४උฤʣ ऩ༰ΞϓϦͷ࡞੒ ‣ $SFBUFBOFX9DPEFQSPKFDUͰNBD04"QQΛબ୒͢Δ

Slide 19

Slide 19 text

ػೳ֦ுͷ௥Ճ ‣ 'JMF/FX5BSHFU'JOEFS4ZOD&YUFOTJPOΛબ୒ͯ͠௥Ճ͢Δ 'JOEFS4ZOD&YUFOTJPO࣮૷ͷجຊʢԼ४උฤʣ

Slide 20

Slide 20 text

ػೳ֦ுͷ௥Ճ ‣ 'JMF/FX5BSHFU'JOEFS4ZOD&YUFOTJPOΛબ୒ͯ͠௥Ճ͢Δ ‣ Լͷը૾ͷΑ͏ͳΞϥʔτ͕ग़ͨ৔͸"DUJWBUFΛબ୒͢Δ 'JOEFS4ZOD&YUFOTJPO࣮૷ͷجຊʢԼ४උฤʣ

Slide 21

Slide 21 text

ऩ༰ΞϓϦͱػೳ֦ுͷόʔδϣϯͱϏϧυόʔδϣϯΛἧ͑Δ ऩ༰ΞϓϦ ػೳ֦ு 'JOEFS4ZOD&YUFOTJPO࣮૷ͷجຊʢԼ४උฤʣ

Slide 22

Slide 22 text

͜͜·ͰͰɺࠨͷը૾ͷΑ͏ͳιʔεπϦʔʹͳΔ 'JOEFSػೳ֦ு͸'JOEFS4ZODTXJGUͱ͍͏ ϑΝΠϧΛத৺ʹهड़͍ͯ͘͠ 4DIFNFΛऩ༰ΞϓϦʹͯ͠Ϗϧυͨ͠৔߹͸ɺ 'JOEFSػೳ֦ுଆ΋Ϗϧυ͞ΕΔ 4DIFNFΛ'JOEFSػೳ֦ுʹͨ͠৔߹͸ɺ ػೳ֦ுͷΈ͕Ϗϧυ͞ΕΔ 'JOEFS4ZOD&YUFOTJPO࣮૷ͷجຊʢԼ४උฤʣ

Slide 23

Slide 23 text

"'JOEFSػೳ֦ு͕༗ޮʹͳ͍ͬͯΔ͔Ͳ͏͔Λ֬ೝ͢Δํ๏ 'JOEFS4ZOD&YUFOTJPO࣮૷ͷجຊʢػೳผฤʣ if FIFinderSyncController.isExtensionEnabled { // Finderػೳ֦ு͸ڐՄࡁΈ } else { // Finderػೳ֦ு͸ڐՄ͞Ε͍ͯͳ͍ } FIFinderSyncController.showExtensionManagementInterface() "γεςϜ؀ڥઃఆͷ'JOEFSػೳ֦ுͷϖʔδΛ։͘ํ๏

Slide 24

Slide 24 text

"'JOEFSػೳ֦ு͕؂ࢹ͢ΔϑΥϧμΛࢦఆ͢Δํ๏ 'JOEFS4ZOD&YUFOTJPO࣮૷ͷجຊʢػೳผฤʣ class FinderSync: FIFinderSync { override init() { super.init() let targetPath = "/Users/UserName/Desktop" let targetURL = URL(fileURLWithPath: targetPath, isDirectory: true) FIFinderSyncController.default().directoryURLs = [targetURL] } }

Slide 25

Slide 25 text

"؂ࢹԼͷϑΥϧμΛϢʔβ͕ૢ࡞։࢝ʗऴྃͨ͜͠ͱΛݕग़͢Δํ๏ 'JOEFS4ZOD&YUFOTJPO࣮૷ͷجຊʢػೳผฤʣ class FinderSync: FIFinderSync { // ؂ࢹԼͷϑΥϧμΛϢʔβ͕ૢ࡞։࢝ͨ͠ࡍʹݺͼग़͞ΕΔ override func beginObservingDirectory(at url: URL) {} // ؂ࢹԼͷϑΥϧμΛϢʔβ͕ૢ࡞ऴྃͨ͠ࡍʹݺͼग़͞ΕΔ override func endObservingDirectory(at url: URL) {} } url ͸Ϣʔβͷૢ࡞ର৅ͷϑΥϧμύεͰ͋Δ

Slide 26

Slide 26 text

"؂ࢹԼͷΞΠςϜʹόοδΛ͚ͭΔํ๏ 'JOEFS4ZOD&YUFOTJPO࣮૷ͷجຊʢػೳผฤʣ 'JOEFSػೳ֦ுͷ5BSHFUʹ"TTFU$BUBMPHΛ௥Ճ͢Δ "TTFU$BUBMPHʹόοδͷը૾Λ௥Ճ͢Δ *NBHF4J[FºQJYFMT %FWJDFT.BD 4DBMFT4JOHMF4DBMF QY QY

Slide 27

Slide 27 text

"؂ࢹԼͷΞΠςϜʹόοδΛ͚ͭΔํ๏ 'JOEFS4ZOD&YUFOTJPO࣮૷ͷجຊʢػೳผฤʣ 'JOEFS4ZODʹόοδͷొ࿥Λ͢Δ class FinderSync: FIFinderSync { override init() { super.init() FFIFinderSyncController.default() .setBadgeImage(NSImage, label: String?, forBadgeIdentifier: String) } }

Slide 28

Slide 28 text

"؂ࢹԼͷΞΠςϜʹόοδΛ͚ͭΔํ๏ 'JOEFS4ZOD&YUFOTJPO࣮૷ͷجຊʢػೳผฤʣ όοδͱΞΠςϜΛඥ͚ͮΔ class FinderSync: FIFinderSync { override func requestBadgeIdentifier(for url: URL) { FIFinderSyncController.default() .setBadgeIdentifier(String, for: url) } } ຊདྷ͸ url ͔ΒϑΝΠϧͷ৘ใΛऔಘͯ͠ɺόοδ෇༩ͷ൑அ͢Δ ྫ͑͹ɺurl.hasDirectoryPath ͰϑΥϧμ͔Ͳ͏͔ url.pathExtension Ͱ֦ுࢠͷ֬ೝ͕Ͱ͖Δ

Slide 29

Slide 29 text

"ΧελϜίϯςΩετϝχϡʔͷ௥Ճํ๏ 'JOEFS4ZOD&YUFOTJPO࣮૷ͷجຊʢػೳผฤʣ class FinderSync: FIFinderSync { override func menu(for menu: FIMenuKind) -> NSMenu? {} } Swift Ͱ͸ύϥϝʔλ໊Λม͑ͯ΋ಉؔ͡਺ͱͯ͠ίϯύΠϧ͞ΕΔͷͰɺ menu Λ menuKind ʹมߋ͢Δͱѻ͍΍͍͢ override func menu(for menuKind: FIMenuKind) -> NSMenu? {} ௥Ճ͢ΔͨΊͷ"1*ʹ͍ͭͯ

Slide 30

Slide 30 text

"ΧελϜίϯςΩετϝχϡʔͷ௥Ճํ๏ 'JOEFS4ZOD&YUFOTJPO࣮૷ͷجຊʢػೳผฤʣ override func menu(for menuKind: FIMenuKind) -> NSMenu? { let menu = NSMenu(title: "") // ϑΥϧμͷഎܠ্ͰӈΫϦοΫΛͨ͠ͱ͖ if menuKind == .contextualMenuForContainer { menu.addItem(withTitle: String, action: Selector?, keyEquivalent: String) } // ϑΝΠϧΛӈΫϦοΫͨ͠ͱ͖ if menuKind == .contextualMenuForItems { menu.addItem(withTitle: String, action: Selector?, keyEquivalent: String) } return menu }

Slide 31

Slide 31 text

"ΧελϜπʔϧόʔϘλϯͷ௥Ճํ๏ 'JOEFS4ZOD&YUFOTJPO࣮૷ͷجຊʢػೳผฤʣ πʔϧόʔϘλϯͷ໊લɺπʔϧνοϓɺΞΠίϯը૾Λઃఆ͢Δ class FinderSync: FIFinderSync { // πʔϧόʔϘλϯͷ໊લ override var toolbarItemName: String {} // πʔϧόʔϘλϯͷπʔϧνοϓ override var toolbarItemToolTip: String {} // πʔϧόʔϘλϯͷΞΠίϯը૾ override var toolbarItemImage: NSImage {} }

Slide 32

Slide 32 text

"ΧελϜπʔϧόʔϘλϯͷ௥Ճํ๏ 'JOEFS4ZOD&YUFOTJPO࣮૷ͷجຊʢػೳผฤʣ ϘλϯΛԡͨ͠ͱ͖ʹදࣔ͢ΔίϯςΩετϝχϡʔͷࢦఆΛ͢Δ override func menu(for menuKind: FIMenuKind) -> NSMenu? { let menu = NSMenu(title: "") // ΧελϜπʔϧόʔϘλϯΛΫϦοΫͨ͠ͱ͖ if menuKind == .toolbarItemMenu { menu.addItem(withTitle: String, action: Selector?, keyEquivalent: String) } return menu }

Slide 33

Slide 33 text

"ίϯςΩετϝχϡʔʹऩೲΞϓϦͷΞΠίϯΛදࣔͤ͞Δํ๏ 'JOEFS4ZOD&YUFOTJPO࣮૷ͷجຊʢػೳผฤʣ let menuItem = NSMenuItem(title: String, action: Selector?, keyEquivalent: String) let id = "ऩೲΞϓϦͷBundle Identifier" if let url = NSWorkspace.shared.urlForApplication(withBundleIdentifier: id) { menuItem.image = NSWorkspace.shared.icon(forFile: url.path) }

Slide 34

Slide 34 text

"Ϣʔβ͕ૢ࡞͍ͯ͠ΔϑΥϧμͷ63-Λऔಘ͢Δํ๏ 'JOEFS4ZOD&YUFOTJPO࣮૷ͷجຊʢػೳผฤʣ let targetedURL = FIFinderSyncController.default().targetedURL() let itemURLs = FIFinderSyncController.default().selectedItemURLs() "Ϣʔβ͕બ୒ͨ͠ϑΝΠϧͷ63-Λऔಘ͢Δํ๏ Ϣʔβ͕ϑΝΠϧΛબ୒ͤͣɺϑΥϧμ্ͰӈΫϦοΫͨ͠৔߹͸ɺ .selectedItemURLs() ͸ .targetedURL() ͱಉ͡URLʹͳΔ

Slide 35

Slide 35 text

'JOEFS4ZOD&YUFOTJPOͷσόοάํ๏

Slide 36

Slide 36 text

'JOEFS4ZOD&YUFOTJPOͷσόοάํ๏ ·ͣɺ4XJGUQSJOU Λ࢖͏͜ͱ͸Ͱ͖ͳ͍ ػೳ֦ுͷ1SPDFTTΛ"UUBDIͯ͠΋ඪ४ग़ྗʹදࣔ͞Εͳ͍ ‣ 9DPEFͰϒϨʔΫϙΠϯτΛ࢖͏ํ๏ ‣ /4-PHͱίϯιʔϧΛ࢖͏ํ๏ ‣ (6*Λ࢖͏ํ๏

Slide 37

Slide 37 text

"9DPEFͰϒϨʔΫϙΠϯτΛ࢖͏ํ๏ 'JOEFS4ZOD&YUFOTJPOͷσόοάํ๏ 1.Clean Build Folder Λ࣮ߦ͢Δ 2.Terminal ͳͲͰ killall Finder Λୟ͘ 3.ऩ༰ΞϓϦΛϏϧυˍ࣮ߦͯ͠ɺγεςϜ؀ڥઃఆͰػೳ֦ுΛ༗ޮʹ͓ͯ͘͠ 4.ίʔυͷ೚ҙͷՕॴʹϒϨʔΫϙΠϯτΛ഑ஔ͢Δ 5.Finder Sync Extension ͷλʔήοτΛબΜͰϏϧυ͚ͩ͢Δʢ࣮ߦ͸͠ͳ͍ʣ 6.Finder Ͱ؂ࢹର৅ͷϑΥϧμΛ։͘ 7.Debug -> Attach to process Ͱ࠷΋൪߸ͷେ͖͍ process Λ Attach ͢Δ 8.௨ৗͷϒϨʔΫϙΠϯτΛ༻͍ͨσόοάΛߦ͏ 9.σόοά͕ࡁΜͩΒ Debug -> Detach from ʓʓ Λͯ͠ Detach ͢Δ

Slide 38

Slide 38 text

"/4-PHͱίϯιʔϧΛ࢖͏ํ๏ 'JOEFS4ZOD&YUFOTJPOͷσόοάํ๏ ίʔυ಺ͷඪ४ग़ྗ͍ͨ͠ՕॴͰ Swift.print() ͷ୅ΘΓʹ NSLog() Λ࢖͏ ͜ͷͱ͖ɺग़ྗ͢Δจࣈͷઌ಄ʹֆจࣈΛ͚͓ͭͯ͘ NSLog("# \(url.path)") NSLog("$ check point")

Slide 39

Slide 39 text

"/4-PHͱίϯιʔϧΛ࢖͏ํ๏ 'JOEFS4ZOD&YUFOTJPOͷσόοάํ๏ Console.app Λ։͖ɺݕࡧόʔʹֆจࣈΛೖྗ͢Δʢෳ਺ಉ࣌ʹೖྗ͕Մೳʣ πʔϧόʔͷʮ։࢝ʯΛԡͤ͹ NSLog() ͷग़ྗΛัଊͰ͖ΔΑ͏ʹͳΔ $POTPMFBQQ

Slide 40

Slide 40 text

"(6*Λ࢖͏ํ๏ ‣ /4"MFSUΛ࢖͏ ‣ ϩʔΧϧ௨஌ʢ6TFS/PUJpDBUJPOTʣΛ࢖͏ 'JOEFS4ZOD&YUFOTJPOͷσόοάํ๏

Slide 41

Slide 41 text

"(6*Λ࢖͏ํ๏/4"MFSU 'JOEFS4ZOD&YUFOTJPOͷσόοάํ๏ DispatchQueue.main.async { let alert = NSAlert() alert.alertStyle = .informational alert.messageText = "check point" alert.runModal() } ಈ࡞֬ೝ͍ͨ͠ՕॴʹҎԼͷΑ͏ͳ/4"MFSUදࣔͷίʔυΛهड़͢Δ

Slide 42

Slide 42 text

"(6*Λ࢖͏ํ๏ϩʔΧϧ௨஌ʢ6TFS/PUJpDBUJPOTʣ 'JOEFS4ZOD&YUFOTJPOͷσόοάํ๏ ऩ༰ΞϓϦଆͰ௨஌ηϯλʔͷར༻ڐՄͷ֬ೝΛ͢Δ UNUserNotificationCenter.current() .requestAuthorization(options: [.alert]) { granted, error in Swift.print(granted) // true ͳΒڐՄࡁΈ }

Slide 43

Slide 43 text

"(6*Λ࢖͏ํ๏ϩʔΧϧ௨஌ʢ6TFS/PUJpDBUJPOTʣ 'JOEFS4ZOD&YUFOTJPOͷσόοάํ๏ 'JOEFSػೳ֦ுଆͰϩʔΧϧ௨஌Λදࣔͤ͞ΔίʔυΛهड़͢Δ private func postUserNotification(subtitle: String, body: String) { let content = UNMutableNotificationContent() content.title = "SampleExtension" content.subtitle = subtitle content.body = body let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil) UNUserNotificationCenter.current() .add(request, withCompletionHandler: nil) } // ಈ࡞֬ೝ͍ͨ͠Օॴʹهड़ postUserNotification(subtitle: "check url", body: url.path)

Slide 44

Slide 44 text

ศརπʔϧ࡞੒࣌ͷ5JQT

Slide 45

Slide 45 text

ศརπʔϧ࡞੒࣌ͷ5JQT "63-͔ΒϑΝΠϧ໊ʗϑΥϧμ໊Λऔಘ͢Δ extension URL { var fileName: String { return self.deletingPathExtension().lastPathComponent } } URL.deletingPathExtension() Λ࢖͏ͱ֦ுࢠΛআ͍ͨ URL ͕औಘͰ͖Δ

Slide 46

Slide 46 text

ศརπʔϧ࡞੒࣌ͷ5JQT "ϑΝΠϧૢ࡞͸'JMF.BOBHFSΛ࢖͍౗͢ // ϑΥϧμͷ࡞੒ FileManager.default.createDirectory(at: URL, withIntermediateDirectories: Bool) // ϑΝΠϧͷҠಈ or ϦωʔϜ FileManager.default.moveItem(at: URL, to: URL) // ϑΝΠϧͷ࡟আ FileManager.default.removeItem(at: URL)

Slide 47

Slide 47 text

ศརπʔϧ࡞੒࣌ͷ5JQT "ϑΝΠϧͷ৘ใΛऔಘ͢Δ let url = URL(fileURLWithPath: "/Sample.txt") let attributes = try? FileManager.default.attributesOfItem(atPath: url.path) // ࡞੒೔ let creationDate = attributes?[FileAttributeKey.creationDate] as? Date // มߋ೔ let modDate = attributes?[FileAttributeKey.modificationDate] as? Data // ϑΝΠϧΦʔφʔ໊ let ownerName = attributes?[FileAttributeKey.ownerAccountName] as? String // ϑΝΠϧαΠζʢByteʣ let fileSize = attributes?[FileAttributeKey.size] as? NSNumber // ࢀর͞Εͨճ਺ let refCount = attributes?[FileAttributeKey.referenceCount] as? NSNumber

Slide 48

Slide 48 text

ศརπʔϧ࡞੒࣌ͷ5JQT "/4"MFSUɾ/40QFO1BOFMɾ/44BWF1BOFMΛ׆༻͢Δ ‣ BDDFTTPSZ7JFXΛ࢖ͬͯಠࣗͷΠϯλʔϑΣʔεΛ௥Ճ͢Δ let alert = NSAlert() alert.messageText = "͜Μʹͪ͸" alert.accessoryView = NSView() //%ಠࣗͷView alert.runModal() "DDFTTPSZ7JFX

Slide 49

Slide 49 text

ศརπʔϧ࡞੒࣌ͷ5JQT "/4"MFSUɾ/40QFO1BOFMɾ/44BWF1BOFMΛ׆༻͢Δ ‣ BDDFTTPSZ7JFX಺ͷςΩετϑΟʔϧυͰ͸Χοτɾίϐʔɾϖʔετ ͳͲͷγϣʔτΧοτ͕ޮ͔ͳ͍ͷͰɺ͓·͡ͳ͍͕ඞཁ extension NSTextField { open override func performKeyEquivalent(with event: NSEvent) -> Bool { let flags = event.modifierFlags.intersection(.deviceIndependentFlagsMask) if flags == [.command] { let selector: Selector switch event.charactersIgnoringModifiers?.lowercased() { case "x": selector = #selector(NSText.cut(_:)) case "c": selector = #selector(NSText.copy(_:)) case "v": selector = #selector(NSText.paste(_:)) case "a": selector = #selector(NSText.selectAll(_:)) case "z": selector = Selector(("undo:")) default: return super.performKeyEquivalent(with: event) } return NSApp.sendAction(selector, to: nil, from: self) } else if flags == [.shift, .command] { if event.charactersIgnoringModifiers?.lowercased() == "z" { return NSApp.sendAction(Selector(("redo:")), to: nil, from: self) } self.undoManager?.undo() } return super.performKeyEquivalent(with: event) } }

Slide 50

Slide 50 text

ศརπʔϧ࡞੒࣌ͷ5JQT "/4"MFSUɾ/40QFO1BOFMɾ/44BWF1BOFMΛ׆༻͢Δ ‣ BDDFTTPSZ7JFX಺ͷςΩετϑΟʔϧυͰ͸Χοτɾίϐʔɾϖʔετ ͳͲͷγϣʔτΧοτ͕ޮ͔ͳ͍ͷͰɺ͓·͡ͳ͍͕ඞཁ extension NSTextField { open override func performKeyEquivalent(with event: NSEvent) -> Bool { let flags = event.modifierFlags.intersection(.deviceIndependentFlagsMask) if flags == [.command] { let selector: Selector switch event.charactersIgnoringModifiers?.lowercased() { case "x": selector = #selector(NSText.cut(_:)) case "c": selector = #selector(NSText.copy(_:)) case "v": selector = #selector(NSText.paste(_:)) case "a": selector = #selector(NSText.selectAll(_:)) case "z": selector = Selector(("undo:")) default: return super.performKeyEquivalent(with: event) } return NSApp.sendAction(selector, to: nil, from: self) } else if flags == [.shift, .command] { if event.charactersIgnoringModifiers?.lowercased() == "z" { return NSApp.sendAction(Selector(("redo:")), to: nil, from: self) } self.undoManager?.undo() } return super.performKeyEquivalent(with: event) } } ϝχϡʔόʔ͕ͳ͘ʮฤूʯܥͷ FirstResponder ͱͷܨ͕ΓΛ ࣋ͨͳ͍ͨΊɺNSTextField ͷ performKeyEquivalent Ͱ γϣʔτΧοτ͝ͱʹॲཧΛׂΓ౰ͯΔඞཁ͕͋Δ

Slide 51

Slide 51 text

'JOEFS4ZOD&YUFOTJPOͷ஫ҙ఺

Slide 52

Slide 52 text

'JOEFS4ZOD&YUFOTJPOͷ஫ҙ఺ 'JOEFSػೳ֦ு͸Ϣʔβ͕બ୒ͨ͠ϑΝΠϧͰ͋Δʹ΋͔͔ΘΒͣɺ ಡΈॻ͖ݖݶʢ&OUJUMFNFOUTʣ͕௨༻͠ͳ͍৔߹͕͋Δ ‣ ҎԼͷ̎ͭ͸Ϣʔβ͕બ୒ͨ͠ΞΠςϜͷ63-Λฦ͕͢ɺॻ͖׵͑ͨΓ ࡟আͨ͠Γ͸ͦͷ··Ͱ͸Ͱ͖ͳ͍ FIFinderSyncController.default().targetedURL() FIFinderSyncController.default().selectedItemURLs()() ‣ 'JMF"DDFTTͰDPNBQQMFTFDVSJUZpMFTVTFSTFMFDUFESFBEXSJUFΛ :&4ʹͯ͠΋ޮ͖໨͕ͳ͍

Slide 53

Slide 53 text

'JOEFS4ZOD&YUFOTJPOͷ஫ҙ఺ 'JOEFSػೳ֦ு͸Ϣʔβ͕બ୒ͨ͠ϑΝΠϧͰ͋Δʹ΋͔͔ΘΒͣɺ ಡΈॻ͖ݖݶʢ&OUJUMFNFOUTʣ͕௨༻͠ͳ͍৔߹͕͋Δ 㾎 /40QFO1BOFM΍/44BWF1BOFMΛ࢖ͬͯϑΝΠϧύεΛऔಘ͢Ε͹ ಡΈॻ͖ݖݶ͕௨༻͢Δ 㾎 "QQ4BOECPY5FNQPSBSZ&YDFQUJPOͰ͋Δ DPNBQQMFTFDVSJUZUFNQPSBSZFYDFQUJPOpMFTBCTPMVUFQBUISFBEXSJUF ·ͨ͸ DPNBQQMFTFDVSJUZUFNQPSBSZFYDFQUJPOpMFTIPNFSFMBUJWFQBUISFBEXSJUF Λ༻͍ͯ؂ࢹ͢ΔϑΥϧμͷύεΛࢦఆ͢Ε͹ಡΈॻ͖͕ՄೳʹͳΔ

Slide 54

Slide 54 text

'JOEFS4ZOD&YUFOTJPOͷ஫ҙ఺ 'JOEFSػೳ֦ு͸Ϣʔβ͕બ୒ͨ͠ϑΝΠϧͰ͋Δʹ΋͔͔ΘΒͣɺ ಡΈॻ͖ݖݶʢ&OUJUMFNFOUTʣ͕௨༻͠ͳ͍৔߹͕͋Δ 㾎 /40QFO1BOFM΍/44BWF1BOFMΛ࢖ͬͯϑΝΠϧύεΛऔಘ͢Ε͹ ಡΈॻ͖ݖݶ͕௨༻͢Δ 㾎 "QQ4BOECPY5FNQPSBSZ&YDFQUJPOͰ͋Δ DPNBQQMFTFDVSJUZUFNQPSBSZFYDFQUJPOpMFTBCTPMVUFQBUISFBEXSJUF ·ͨ͸ DPNBQQMFTFDVSJUZUFNQPSBSZFYDFQUJPOpMFTIPNFSFMBUJWFQBUISFBEXSJUF Λ༻͍ͯ؂ࢹ͢ΔϑΥϧμͷύεΛࢦఆ͢Ε͹ಡΈॻ͖͕ՄೳʹͳΔ App Store Ͱ഑৴Ͱ͖Δ͔͸৹ࠪһͱͷަব࣍ୈ Temporary Exception Λ෇༩͢Δ৔߹͸ɺͳͥͦΕ͕ඞཁͳͷ͔ ৹ࠪһʹઆ໌͢Δඞཁ͕͋Δ

Slide 55

Slide 55 text

'JOEFS4ZOD&YUFOTJPOͷ஫ҙ఺ "QQ4BOECPYԼͰ؂ࢹ͢ΔϑΥϧμΛࢦఆ͢Δͷ͸໽հ ‣ "QQ4BOECPY༗ޮ࣌͸ૉ௚ͳύεΛऔಘͰ͖ͳ͍ let homeDirectoryPath = NSHomeDirectory() let desktopPath = FileManager.default .urls(for: .desktopDirectory, in: .userDomainMask) .first!.path // App Sandbox ແޮ࣌ /Users/UserName /Users/UserName/Desktop // App Sandbox ༗ޮ࣌ /Users/UserName/Library/Containers/AppBundleIdentifier/Data /Users/UserName/Library/Containers/AppBundleIdentifier/Data/Desktop

Slide 56

Slide 56 text

'JOEFS4ZOD&YUFOTJPOͷ஫ҙ఺ "QQ4BOECPYԼͰ؂ࢹ͢ΔϑΥϧμΛࢦఆ͢Δͷ͸໽հ ‎ %BSXJOͷ"1*Ͱڧ੍తʹϢʔβͷϗʔϜϑΥϧμύεΛऔಘՄೳ if let pw = getpwuid(getuid()), let home = pw.pointee.pw_dir { let homePath = FileManager.default .string(withFileSystemRepresentation: home, length: strlen(home)) // homePath => /Users/UserName let targetURL = URL(fileURLWithPath: homePath) .appendingPathComponent("Desktop") FIFinderSyncController.default().directoryURLs = [targetURL] }

Slide 57

Slide 57 text

'JOEFS4ZOD&YUFOTJPOͷ஫ҙ఺ ΧελϜπʔϧόʔϘλϯ͸Ϣʔβ͕ҙਤతʹઃఆ͠ͳ͍ͱදࣔ͞Εͳ͍

Slide 58

Slide 58 text

'JOEFS4ZOD&YUFOTJPOͷ஫ҙ఺ 'JOEFSػೳ֦ுͲ͏͕͠ڝ߹͢Δ͜ͱ͕͋Δ ‣ ؂ࢹର৅ͱ͍ͯ͠ΔϑΥϧμʢ͓Αͼͦͷ֊૚Լʣ͕ॏͳ͍ͬͯΔ৔߹ɺ ઌʹ༗ޮʹ͞Εͨ'JOEFSػೳ֦ு͕༏ઌ͞ΕΔ ‎ ڝ߹ͨ͠৔߹ɺόοδͷදࣔػೳ͕ແޮʹͳΔ γεςϜ؀ڥઃఆͰ Finder ػೳ֦ுͷ༗ޮແޮΛखಈ੾Γସ͑ ͯ͠ɺ༏ઌॱҐΛௐ੔͢Δඞཁ͕͋Δ

Slide 59

Slide 59 text

ศརπʔϧͷ࣮૷ྫ

Slide 60

Slide 60 text

ศརπʔϧͷ࣮૷ྫ̍ 4DBMF)FMQFSʙ։ൃͰͷը૾ϦαΠζΛख఻͏πʔϧʙ ιʔεˍμ΢ϯϩʔυɿIUUQTHJUIVCDPN,ZPNF4DBMF)FMQFS ը૾ϑΝΠϧͷ໊લͷ຤ඌʹʮ!Yʯ΍ʮ!Yʯ͕͍͍ͭͯΔ ৔߹ɺͦΕΛ໌ࣔ͢Δόοδ͕දࣔ͞ΕɺίϯςΩετϝχϡʔ ͔Β̍ഒ΍̎ഒαΠζͷը૾Λੜ੒Ͱ͖Δ όοδදࣔ ίϯςΩετϝχϡʔʹΑΔλεΫ࣮ߦ

Slide 61

Slide 61 text

ศརπʔϧͷ࣮૷ྫ̍4DBMF)FMQFSσϞ ಈը

Slide 62

Slide 62 text

ศརπʔϧͷ࣮૷ྫ̎ 3FOBNF)FMQFSʙ04ඪ४ͷ໊শมߋͷ଍Γͳ͍ॴΛิ͏πʔϧʙ ιʔεˍμ΢ϯϩʔυɿIUUQTHJUIVCDPN,ZPNF3FOBNF)FMQFS ෳ਺ϑΝΠϧબ୒࣌ɺίϯςΩετϝχϡʔ͔Βಛఆͷϧʔϧʹ ैͬͯ࿈൪ͰϦωʔϜΛߦ͑Δ ίϯςΩετϝχϡʔʹΑΔλεΫ࣮ߦ /4"MFSU͓Αͼ"DDFTTPSZ7JFXΛ׆༻

Slide 63

Slide 63 text

ศརπʔϧͷ࣮૷ྫ̍3FOBNF)FMQFSσϞ ಈը

Slide 64

Slide 64 text

ศརπʔϧͷ࣮૷ྫ̏ /FX$BOWBTʙ1SFWJFXBQQͷᙱ͍ॴʹखΛಧ͔ͤΔπʔϧʙ ιʔεˍμ΢ϯϩʔυɿIUUQTHJUIVCDPN,ZPNF/FX$BOWBT σεΫτοϓ΍'JOEFS্ʹ͓͍ͯɺίϯςΩετϝχϡʔ΍ πʔϧόʔϘλϯ͔Βແ஍ͷը૾ϑΝΠϧΛੜ੒ͯ͠ 1SFWJFXBQQͰ։͚Δ ίϯςΩετϝχϡʔʹΑΔλεΫ࣮ߦ ΧελϜπʔϧόʔͷ௥Ճ /44BWF1BOFM͓Αͼ"DDFTTPSZ7JFXΛ׆༻

Slide 65

Slide 65 text

ศརπʔϧͷ࣮૷ྫ̍/FX$BOWBTσϞ ಈը

Slide 66

Slide 66 text

·ͱΊ

Slide 67

Slide 67 text

·ͱΊ 'JOEFS4ZOD&YUFOTJPO͸ৗறܕϢʔςΟϦςΟπʔϧͷ࡞੒ʹͽͬͨΓʂ ‣ ࢦఆͨ͠ϑΥϧμ಺ͷঢ়ଶ؂ࢹ ‣ ϑΝΠϧ΍ϑΥϧμ΁ͷόοδͷ෇༩ ‣ ΧελϜίϯςΩετϝχϡʔʹΑΔλεΫ࣮ߦ ‣ ΧελϜπʔϧόʔϘλϯʹΑΔλεΫ࣮ߦ σόοάํ๏͕ಛघ "QQ,JUͷμΠΞϩάܥ6*Λ࢖ָ࣮ͬͯͯ͠૷ ϑΝΠϧͷಡΈॻ͖ݖݶʹ͸஫ҙʂ

Slide 68

Slide 68 text

'JOEFS4ZOD&YUFOTJPOͰ ศརπʔϧΛ࡞Δͱ͜Ζ͔Βɺ NBD04ΞϓϦ։ൃ࢝ΊͯΈ·ͤΜ͔ʁ 5IBOL:PV