Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Enhancing Applications with Accessibility API
Search
Kishikawa Katsumi
March 20, 2024
Programming
6.5k
3
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Enhancing Applications with Accessibility API
Kishikawa Katsumi
March 20, 2024
More Decks by Kishikawa Katsumi
See All by Kishikawa Katsumi
Accessibility APIでテキストの内容、位置、サイズを取得する実装パターン
kishikawakatsumi
0
90
ゲームコントローラやキーボードのファームウェアをSwiftで書く
kishikawakatsumi
1
290
OCRを使ってゲームのアイテムをデータ化する
kishikawakatsumi
0
200
Running Swift without an OS
kishikawakatsumi
0
1.2k
浮動小数の比較について
kishikawakatsumi
0
640
Automatic Grammar Agreementと Markdown Extended Attributes について
kishikawakatsumi
0
280
愛される翻訳の秘訣
kishikawakatsumi
3
510
Private APIの呼び出し方
kishikawakatsumi
3
1.1k
iOSでSVG画像を扱う
kishikawakatsumi
0
270
Other Decks in Programming
See All in Programming
golang-bob.pdf
iwatsukayura
0
120
Domain-Driven Transformation
hschwentner
2
2.3k
Vue Fes Japan の “作る側”に関わってみて
naokihaba
0
140
見えないものを探る要求要件定義に必要な基本的思考 / invisible-requirement-thinking
minodriven
15
8.2k
自サークルのIdPからログアウトできるようになるまで
taiseiue
1
130
SREの越境 / SRE Collaboration
y0hgi
2
300
Findy - エンジニア向け会社紹介/Findy Company Deck
findyinc
6
400k
UnityでSystem.Net.WebSocketsなWebSocketサーバが動かないのでUnity Monoのコードを覗いてみた / about implementing websocket server with unity mono
drumath2237
1
580
更なる可用性を求めて、5年間運用したKotlinのアプリケーションをGoでリプレイスする話
ken_tunc
0
480
Java 27新機能 / Java 27 new features
kishida
2
220
Verilogで学ぶCPU自作入門
uyuki234
8
4.1k
Agentic Software Factoryに、すごく賢いIF文を。 / super smart IF statement into the Agentic Software Factory.
rkaga
8
2.1k
Featured
See All Featured
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
56
3.5k
Deep Space Network (abreviated)
tonyrice
0
360
The Spectacular Lies of Maps
axbom
PRO
1
1.1k
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
350
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
2.2k
Navigating Algorithm Shifts & AI Overviews - #SMXNext
aleyda
1
1.6k
ラッコキーワード サービス紹介資料
rakko
1
5.3M
How to make the Groovebox
asonas
2
2.5k
Navigating Team Friction
lara
192
16k
Discover your Explorer Soul
emna__ayadi
2
1.3k
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.8k
Technical Leadership for Architectural Decision Making
baasie
3
600
Transcript
&OIBODJOH"QQMJDBUJPOTXJUI "DDFTTJCJMJUZ"1* LJTIJLBXBLBUTVNJ !LJTIJLBXBLBUTVNJ!IBDIZEFSNJP LJTIJLBXBLBUTVNJ
IUUQTDPEFWJTVBMTUVEJPDPNVQEBUFTW@@TUSFBNJOHJOMJOFDIBU
IUUQTDPEFWJTVBMTUVEJPDPNVQEBUFTW@@IPMEUPTQFBLNPEF
*OUFHSBUF"*"TTJTUBOUT*OUP"OZ"QQT r *OZPVSGBWPSJUFUFYUFEJUPS r *OZPVSXFCCSPXTFS r 0SDPNJDSFBEFST
4BNQMF$PEF ➡IUUQTHJUIVCDPNLJTIJLBXBLBUTVNJUSZTXJGU
None
$PSF5FDIOPMPHJFT r 3FUSJFWFTFMFDUFEUFYUGPSBOZBQQMJDBUJPO r .POJUPSHMPCBMLFZNPVTFFWFOUT
"DDFTTJCJMJUZ"1* r "MMPXTBDDFTTUP6*FMFNFOUTPGUIFTZTUFNPSPUIFSBQQMJDBUJPOT r &OBCMFTNBOJQVMBUJPOPG6*FMFNFOUTPGPUIFSBQQMJDBUJPOT
&OBCMJOH"DDFTTJCJMJUZ"1* 4BOECPYBOE1SJWBDZ4FUUJOHT r %JTBCMF4BOECPY r (SBOU"DDFTTJCJMJUZ"1*1FSNJTTJPOT
%JTBCMF4BOECPY
(SBOU"DDFTTJCJMJUZ1FSNJTTJPO 1SJWBDZ4FUUJOHT
(SBOU"DDFTTJCJMJUZ1FSNJTTJPO "9*41SPDFTT5SVTUFE8JUI0QUJPOT @ [kAXTrustedCheckOptionPrompt: true]
(SBOU"DDFTTJCJMJUZ1FSNJTTJPO "9*41SPDFTT5SVTUFE8JUI0QUJPOT @
(SBOU"DDFTTJCJMJUZ1FSNJTTJPO "9*41SPDFTT5SVTUFE8JUI0QUJPOT @
(SBOU"DDFTTJCJMJUZ1FSNJTTJPO "9*41SPDFTT5SVTUFE8JUI0QUJPOT @
#BTJD6TBHF "96*&MFNFOU$PQZ"UUSJCVUF7BMVF @@@
#BTJD6TBHF "96*&MFNFOU$PQZ"UUSJCVUF7BMVF @@@ 5IF6*FMFNFOUDPOUBJOJOHUIFWBMVFZPVXBOUUPSFUSJFWF
#BTJD6TBHF "96*&MFNFOU$PQZ"UUSJCVUF7BMVF @@@ ,FZPGUIFWBMVFZPVXBOUUPSFUSJFWF
#BTJD6TBHF "96*&MFNFOU$PQZ"UUSJCVUF7BMVF @@@ 7BMVFZPVXBOUUPSFUSJFWF
#BTJD6TBHF "96*&MFNFOU$PQZ"UUSJCVUF7BMVF @@@ &SSPSDPEF
3FUSJFWF4FMFDUFE5FYU L"94FMFDUFE5FYU"UUSJCVUF var selectedTextValue: AnyObject? let selectedTextValueError = AXUIElementCopyAttributeValue( focusedElement
as! AXUIElement, kAXSelectedTextAttribute as CFString, &selectedTextValue ) guard let selectedTextValue, selectedTextValueError == .success else { return }
3FUSJFWF4FMFDUFE5FYU L"94FMFDUFE5FYU"UUSJCVUF var selectedTextValue: AnyObject? let selectedTextValueError = AXUIElementCopyAttributeValue( focusedElement
as! AXUIElement, kAXSelectedTextAttribute as CFString, &selectedTextValue ) guard let selectedTextValue, selectedTextValueError == .success else { return }
3FUSJFWF4FMFDUFE5FYU L"94FMFDUFE5FYU"UUSJCVUF var selectedTextValue: AnyObject? let selectedTextValueError = AXUIElementCopyAttributeValue( focusedElement
as! AXUIElement, kAXSelectedTextAttribute as CFString, &selectedTextValue ) guard let selectedTextValue, selectedTextValueError == .success else { return }
4ZTUFN8JEF"96*&MFNFOU "96*&MFNFOU$SFBUF4ZTUFN8JEF let systemWideElement: AXUIElement = AXUIElementCreateSystemWide()
4ZTUFN8JEF"96*&MFNFOU r "DDFTTTZTUFNXJEFLFZCPBSEGPDVT NPVTFDVSTPSQPTJUJPO BOEPUIFS JOGPSNBUJPO r "DDFTTTZTUFNMFWFM6*FMFNFOUTUIBUEPOPUCFMPOHUPBTQFDJ fi D
BQQMJDBUJPO FH NFOVCBS OPUJ fi DBUJPODFOUFS r 3FUSJFWFJOGPSNBUJPOBCPVUUIFDPOUFYUPGUIFDVSSFOUVTFSJOUFSBDUJPO TVDIBTUIFBDUJWFXJOEPXPSUIF6*FMFNFOUUIBUDVSSFOUMZIBTGPDVT
3FUSJFWF'PDVTFE6*&MFNFOU L"9'PDVTFE6*&MFNFOU"UUSJCVUF let systemWideElement = AXUIElementCreateSystemWide() var focusedElement: AnyObject? let
focusedElementError = AXUIElementCopyAttributeValue( systemWideElement, kAXFocusedUIElementAttribute as CFString, &focusedElement )
3FUSJFWF4FMFDUFE5FYU L"94FMFDUFE5FYU"UUSJCVUF var selectedTextValue: AnyObject? let selectedTextValueError = AXUIElementCopyAttributeValue( focusedElement
as! AXUIElement, kAXSelectedTextAttribute as CFString, &selectedTextValue ) guard let selectedTextValue, selectedTextValueError == .success else { return }
None
.POJUPS(MPCBM,FZ&WFOU /4&WFOUBEE(MPCBM.POJUPS'PS&WFOUT NBUDIJOHIBOEMFS open class NSEvent : NSObject, NSCopying, NSCoding
{ open class func addGlobalMonitorForEvents( matching mask: NSEvent.EventTypeMask, handler block: @escaping (NSEvent) -> Void ) -> Any? }
.POJUPS(MPCBM,FZ&WFOU NSEvent.addGlobalMonitorForEvents( matching: [.keyDown] ) { (event) in switch event.type
{ case .keyDown: guard event.modifierFlags.contains([.command, .control]) && event.keyCode == kVK_ANSI_A else { return } ... default: break } }
.POJUPS(MPCBM,FZ&WFOU NSEvent.addGlobalMonitorForEvents( matching: [.keyDown] ) { (event) in switch event.type
{ case .keyDown: guard event.modifierFlags.contains([.command, .control]) && event.keyCode == kVK_ANSI_A else { return } ... default: break } }
.POJUPS(MPCBM,FZ&WFOU $BSCPO"1* *OTUBMM&WFOU)BOEMFS 3FHJTUFS&WFOU)PU,FZ ➡IUUQTHJUIVCDPNLJTIJLBXBLBUTVNJUSZTXJGU r *OTUBMM&WFOU)BOEMFS @@@@@@
r 3FHJTUFS&WFOU)PU,FZ @@@@@@ r $BSCPO"1* r /PBDDFTTJCJMJUZQFSNJTTJPOSFRVJSFE
.POJUPS(MPCBM,FZ&WFOU &WFOU5BQ ➡IUUQTHJUIVCDPNLJTIJLBXBLBUTVNJUSZTXJGU let mask: CGEventMask = (1 << CGEventType.keyDown.rawValue)
let tap = CGEvent.tapCreate( tap: .cgSessionEventTap, place: .headInsertEventTap, options: .defaultTap, eventsOfInterest: mask, callback: { (proxy, type, event, refcon) in ... }, userInfo: UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()) ) let runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, tap, 0) CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, .commonModes) CGEvent.tapEnable(tap: tap!, enable: true)
.POJUPS(MPCBM,FZ&WFOU ➡IUUQTHJUIVCDPNLJTIJLBXBLBUTVNJUSZTXJGU r $BOTUPQFWFOUQSPQBHBUJPO r $BONPEJGZFWFOU &WFOU5BQ
None
3FUSJFWF4FMFDUFE5FYU 'SPN8FC7JFX var selectedTextMarkerRangeValue: AnyObject? let selectedTextMarkerRangeValueError = AXUIElementCopyAttributeValue( focusedElement
as! AXUIElement, "AXSelectedTextMarkerRange" as CFString, &selectedTextMarkerRangeValue ) ... var stringForTextMarkerRangeValue: AnyObject? let stringForTextMarkerRangeValueError = AXUIElementCopyParameterizedAttributeValue( focusedElement as! AXUIElement, "AXStringForTextMarkerRange" as CFString, selectedTextMarkerRangeValue, &stringForTextMarkerRangeValue ) ...
3FUSJFWF4FMFDUFE5FYU 'SPN8FC7JFX var selectedTextMarkerRangeValue: AnyObject? let selectedTextMarkerRangeValueError = AXUIElementCopyAttributeValue( focusedElement
as! AXUIElement, "AXSelectedTextMarkerRange" as CFString, &selectedTextMarkerRangeValue ) ... var stringForTextMarkerRangeValue: AnyObject? let stringForTextMarkerRangeValueError = AXUIElementCopyParameterizedAttributeValue( focusedElement as! AXUIElement, "AXStringForTextMarkerRange" as CFString, selectedTextMarkerRangeValue, &stringForTextMarkerRangeValue ) ...
None
"96*&MFNFOU$PQZ1BSBNFUFSJ[FE"UUSJCVUF7BMVF var selectedTextMarkerRangeValue: AnyObject? let selectedTextMarkerRangeValueError = AXUIElementCopyAttributeValue( focusedElement as!
AXUIElement, "AXSelectedTextMarkerRange" as CFString, &selectedTextMarkerRangeValue ) ... var stringForTextMarkerRangeValue: AnyObject? let stringForTextMarkerRangeValueError = AXUIElementCopyParameterizedAttributeValue( focusedElement as! AXUIElement, "AXStringForTextMarkerRange" as CFString, selectedTextMarkerRangeValue, &stringForTextMarkerRangeValue ) ...
var selectedTextMarkerRangeValue: AnyObject? let selectedTextMarkerRangeValueError = AXUIElementCopyAttributeValue( focusedElement as! AXUIElement,
"AXSelectedTextMarkerRange" as CFString, &selectedTextMarkerRangeValue ) ... var stringForTextMarkerRangeValue: AnyObject? let stringForTextMarkerRangeValueError = AXUIElementCopyParameterizedAttributeValue( focusedElement as! AXUIElement, "AXStringForTextMarkerRange" as CFString, selectedTextMarkerRangeValue, &stringForTextMarkerRangeValue ) ... "96*&MFNFOU$PQZ1BSBNFUFSJ[FE"UUSJCVUF7BMVF
var selectedTextMarkerRangeValue: AnyObject? let selectedTextMarkerRangeValueError = AXUIElementCopyAttributeValue( focusedElement as! AXUIElement,
"AXSelectedTextMarkerRange" as CFString, &selectedTextMarkerRangeValue ) ... var stringForTextMarkerRangeValue: AnyObject? let stringForTextMarkerRangeValueError = AXUIElementCopyParameterizedAttributeValue( focusedElement as! AXUIElement, "AXStringForTextMarkerRange" as CFString, selectedTextMarkerRangeValue, &stringForTextMarkerRangeValue ) ... "96*&MFNFOU$PQZ1BSBNFUFSJ[FE"UUSJCVUF7BMVF
)PXUP,OPX6OEPDVNFOUFE,FZT var selectedTextMarkerRangeValue: AnyObject? let selectedTextMarkerRangeValueError = AXUIElementCopyAttributeValue( focusedElement as!
AXUIElement, "AXSelectedTextMarkerRange" as CFString, &selectedTextMarkerRangeValue ) ... var stringForTextMarkerRangeValue: AnyObject? let stringForTextMarkerRangeValueError = AXUIElementCopyParameterizedAttributeValue( focusedElement as! AXUIElement, "AXStringForTextMarkerRange" as CFString, selectedTextMarkerRangeValue, &stringForTextMarkerRangeValue ) ...
)PXUP,OPX6OEPDVNFOUFE,FZT "96*&MFNFOU$PQZ"UUSJCVUF/BNFT @@
None
None
/PUJGJDBUJPOT var observer: AXObserver? let observerCreateError = AXObserverCreate( app.processIdentifier, {
(observer, element, notification, userData) in ... }, &observer ) AXObserverAddNotification( observer, appElement, kAXSelectedTextChangedNotification as CFString, nil ) CFRunLoopAddSource( CFRunLoopGetCurrent(), AXObserverGetRunLoopSource(observer), .commonModes )
/PUJGJDBUJPOT var observer: AXObserver? let observerCreateError = AXObserverCreate( app.processIdentifier, {
(observer, element, notification, userData) in ... }, &observer ) AXObserverAddNotification( observer, appElement, kAXSelectedTextChangedNotification as CFString, nil ) CFRunLoopAddSource( CFRunLoopGetCurrent(), AXObserverGetRunLoopSource(observer), .commonModes )
/PUJGJDBUJPOT var observer: AXObserver? let observerCreateError = AXObserverCreate( app.processIdentifier, {
(observer, element, notification, userData) in ... }, &observer ) AXObserverAddNotification( observer, appElement, kAXSelectedTextChangedNotification as CFString, nil ) CFRunLoopAddSource( CFRunLoopGetCurrent(), AXObserverGetRunLoopSource(observer), .commonModes ) let app = NSRunningApplication .runningApplications(withBundleIdentifier: "com.apple.TextEdit") .first
/PUJGJDBUJPOT var observer: AXObserver? let observerCreateError = AXObserverCreate( app.processIdentifier, {
(observer, element, notification, userData) in ... }, &observer ) AXObserverAddNotification( observer, appElement, kAXSelectedTextChangedNotification as CFString, nil ) CFRunLoopAddSource( CFRunLoopGetCurrent(), AXObserverGetRunLoopSource(observer), .commonModes )
/PUJGJDBUJPOT var observer: AXObserver? let observerCreateError = AXObserverCreate( app.processIdentifier, {
(observer, element, notification, userData) in ... }, &observer ) AXObserverAddNotification( observer, appElement, kAXSelectedTextChangedNotification as CFString, nil ) CFRunLoopAddSource( CFRunLoopGetCurrent(), AXObserverGetRunLoopSource(observer), .commonModes ) kAXSelectedTextChangedNotification kAXValueChangedNotification ...
.PEJGZ5FYU var isAttributeSettableValue = DarwinBoolean(false) let isAttributeSettableValueError = AXUIElementIsAttributeSettable( element,
kAXValueAttribute as CFString, &isAttributeSettableValue ) guard isAttributeSettableValueError == .success else { return } if isAttributeSettableValue.boolValue { AXUIElementSetAttributeValue( element, kAXValueAttribute as CFString, "\(precedingTextValue)\n\(codeBlock)" as CFString ) return }
.PEJGZ5FYU var isAttributeSettableValue = DarwinBoolean(false) let isAttributeSettableValueError = AXUIElementIsAttributeSettable( element,
kAXValueAttribute as CFString, &isAttributeSettableValue ) guard isAttributeSettableValueError == .success else { return } if isAttributeSettableValue.boolValue { AXUIElementSetAttributeValue( element, kAXValueAttribute as CFString, "\(precedingTextValue)\n\(codeBlock)" as CFString ) return }
.PEJGZ5FYU var isAttributeSettableValue = DarwinBoolean(false) let isAttributeSettableValueError = AXUIElementIsAttributeSettable( element,
kAXValueAttribute as CFString, &isAttributeSettableValue ) guard isAttributeSettableValueError == .success else { return } if isAttributeSettableValue.boolValue { AXUIElementSetAttributeValue( element, kAXValueAttribute as CFString, "\(precedingTextValue)\n\(codeBlock)" as CFString ) return }
None
None
4$4DSFFOTIPU.BOBHFSDBQUVSF*NBHF func captureScreen(rect: CGRect) async throws -> CGImage? {
let content = try await SCShareableContent.excludingDesktopWindows(false, onScreenWindowsOnly: false) guard let mainDisplayID = NSScreen.main?.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? CGDirectDisplayID, let display = content.displays.first(where: { $0.displayID == mainDisplayID }) else { return nil } let streamConfig = SCStreamConfiguration() streamConfig.captureResolution = .automatic streamConfig.sourceRect = rect streamConfig.preservesAspectRatio = true streamConfig.scalesToFit = true streamConfig.capturesAudio = false streamConfig.excludesCurrentProcessAudio = true let filter = SCContentFilter(display: display, excludingWindows: []) let capturedImage = try await SCScreenshotManager.captureImage( contentFilter: filter, configuration: streamConfig ) return capturedImage }
3FUSJFWFBO*NBHF $(8JOEPX-JTU$SFBUF*NBHF @@@@ func captureScreen(rect: CGRect) -> CGImage? { let
screenShot = CGWindowListCreateImage( rect, [.optionOnScreenOnly, .excludeDesktopElements], kCGNullWindowID, [.boundsIgnoreFraming, .bestResolution] ) return screenShot }
(SBOU4DSFFO3FDPSEJOH1FSNJTTJPO 1SJWBDZ4FUUJOHT
(SBOU4DSFFO3FDPSEJOH1FSNJTTJPO $(1SF fl JHIU4DSFFO$BQUVSF"DDFTT r $(1SF fl JHIU4DSFFO$BQUVSF"DDFTT r
*OEJDBUFTXIFUIFSTDSFFOSFDPSEJOHJTBMMPXFE r $(3FRVFTU4DSFFO$BQUVSF"DDFTT r 1SFTFOUUIFTZTUFNQSPNQU
&YUSBDUUIF5FYUGSPNBO*NBHF ϒϥοΫδϟοΫʹΑΖ͘͠ ࠤ౻लๆ
7JTJPO,JU ϒϥοΫδϟοΫʹΑΖ͘͠ ࠤ౻लๆ
&YUSBDUUIF5FYUGSPNBO*NBHF *NBHF"OBMZ[FS let analyzer = ImageAnalyzer() let analysis = try
await analyzer.analyze( image, orientation: .up, configuration: configuration ) let transcript = analysis.transcript
&YUSBDUUIF5FYUGSPNBO*NBHF *NBHF"OBMZ[FS let analyzer = ImageAnalyzer() let analysis = try
await analyzer.analyze( image, orientation: .up, configuration: configuration ) let transcript = analysis.transcript
None
8SBQ6Q r 8JOEPX$IBU"TTJTUBOU r #BTJD6TBHFPG"DDFTTJCJMJUZ"1* r *OMJOF$IBU"TTJTUBOU r /PUJ fi
DBUJPOT r .PEJGZ$POUFOU r $PNJD5SBOTMBUPS r 4DSFFO$BQUVSF"1*BOE0$3 r "96*&MFNFOU*OTQFDUPS r *OTQFDU"UUSJCVUF/BNFPG"96*&MFNFOU
4BNQMF$PEF ➡IUUQTHJUIVCDPNLJTIJLBXBLBUTVNJUSZTXJGU
3FGFSFODFT r 4BNQMF$PEF IUUQTHJUIVCDPNLJTIJLBXBLBUTVNJUSZTXJGU r "DDFTTJOHUFYUWBMVFGSPNBOZ4ZTUFNXJEF"QQMJDBUJPOWJB"DDFTTJCJMJUZ "1* IUUQTNBDEFWFMPQFSTXPSEQSFTTDPNBDDFTTJOHUFYUWBMVFGSPNBOZTZTUFNXJEFBQQMJDBUJPOWJB BDDFTTJCJMJUZBQJ r
)PXUPHFUTFMFDUFEUFYUBOEJUTDPPSEJOBUFTGSPNBOZTZTUFNXJEFBQQMJDBUJPOVTJOH "DDFTTJCJMJUZ "1* IUUQTNBDEFWFMPQFSTXPSEQSFTTDPNIPXUPHFUTFMFDUFEUFYUBOEJUTDPPSEJOBUFTGSPNBOZTZTUFNXJEF BQQMJDBUJPOVTJOHBDDFTTJCJMJUZBQJ r 8IZBSFOUUIFNPTUVTFGVM.BDBQQTPOUIF"QQ4UPSF IUUQTBMJOQBOBJUJVDPNCMPHBQQTPVUTJEFBQQTUPSF r "MMBCPVUNBD04FWFOUPCTFSWBUJPO IUUQTEPDTHPPHMFDPNQSFTFOUBUJPOEO&BJ16EVIWKLTS%735D+B&6-C488IU7E()'@946IUNMQSFTFOU