diff --git a/.gitmodules b/.gitmodules index 6ac43d2..5ba2584 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,9 +10,9 @@ [submodule "lib/cocoahttpserver"] path = lib/cocoahttpserver url = https://github.com/robbiehanson/CocoaHTTPServer.git -[submodule "lib/PublicAutomation"] - path = lib/PublicAutomation - url = https://github.com/TestingWithFrank/PublicAutomation.git [submodule "lib/AnyJSON"] path = lib/AnyJSON url = https://github.com/mattt/AnyJSON.git +[submodule "lib/KIF"] + path = lib/KIF + url = https://github.com/kif-framework/KIF.git diff --git a/Frank.xcodeproj/project.pbxproj b/Frank.xcodeproj/project.pbxproj index a4aafda..30a336e 100644 --- a/Frank.xcodeproj/project.pbxproj +++ b/Frank.xcodeproj/project.pbxproj @@ -22,6 +22,16 @@ 30228E221642161200B1F9E7 /* AppCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C1DD76A12BADFE100E10B8C /* AppCommand.m */; }; 30228E261642161200B1F9E7 /* EnginesCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = D6FA01B714283C4F00576AEE /* EnginesCommand.m */; }; 30228E281642161200B1F9E7 /* ExitCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = A91F3AA515F6E456003F434F /* ExitCommand.m */; }; + 3023E0F41867D60500E2EA70 /* KIFTypist.h in Headers */ = {isa = PBXBuildFile; fileRef = 3023E0F21867D60500E2EA70 /* KIFTypist.h */; }; + 3023E0F51867D60500E2EA70 /* KIFTypist.m in Sources */ = {isa = PBXBuildFile; fileRef = 3023E0F31867D60500E2EA70 /* KIFTypist.m */; }; + 3023E0FE1867D62200E2EA70 /* CGGeometry-KIFAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3023E0F61867D62200E2EA70 /* CGGeometry-KIFAdditions.h */; }; + 3023E0FF1867D62200E2EA70 /* CGGeometry-KIFAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 3023E0F71867D62200E2EA70 /* CGGeometry-KIFAdditions.m */; }; + 3023E1001867D62200E2EA70 /* UIAccessibilityElement-KIFAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3023E0F81867D62200E2EA70 /* UIAccessibilityElement-KIFAdditions.h */; }; + 3023E1011867D62200E2EA70 /* UIAccessibilityElement-KIFAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 3023E0F91867D62200E2EA70 /* UIAccessibilityElement-KIFAdditions.m */; }; + 3023E1021867D62200E2EA70 /* UIApplication-KIFAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3023E0FA1867D62200E2EA70 /* UIApplication-KIFAdditions.h */; }; + 3023E1031867D62200E2EA70 /* UIApplication-KIFAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 3023E0FB1867D62200E2EA70 /* UIApplication-KIFAdditions.m */; }; + 3023E1041867D62200E2EA70 /* UIView-KIFAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3023E0FC1867D62200E2EA70 /* UIView-KIFAdditions.h */; }; + 3023E1051867D62200E2EA70 /* UIView-KIFAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 3023E0FD1867D62200E2EA70 /* UIView-KIFAdditions.m */; }; 302B80411646DE02000F9861 /* NSImage+Frank.h in Headers */ = {isa = PBXBuildFile; fileRef = 302B803F1646DE02000F9861 /* NSImage+Frank.h */; }; 302B80421646DE02000F9861 /* NSImage+Frank.m in Sources */ = {isa = PBXBuildFile; fileRef = 302B80401646DE02000F9861 /* NSImage+Frank.m */; }; 3034CD4B1794CCE000A3794F /* FEXTableRow.h in Headers */ = {isa = PBXBuildFile; fileRef = 3034CD491794CCE000A3794F /* FEXTableRow.h */; }; @@ -71,6 +81,8 @@ 30AC65A4165C39FF00DEB0AB /* FrankMac-Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = 30AC65A3165C39FF00DEB0AB /* FrankMac-Prefix.pch */; }; 30AC65A7165C3A4D00DEB0AB /* OSXKeyboardCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 30AC65A5165C3A4D00DEB0AB /* OSXKeyboardCommand.h */; }; 30AC65A8165C3A4D00DEB0AB /* OSXKeyboardCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 30AC65A6165C3A4D00DEB0AB /* OSXKeyboardCommand.m */; }; + 30B7346E187EF964000136CE /* UITouch-KIFAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30B7346C187EF964000136CE /* UITouch-KIFAdditions.h */; }; + 30B7346F187EF964000136CE /* UITouch-KIFAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 30B7346D187EF964000136CE /* UITouch-KIFAdditions.m */; }; 30C544B1167E4D9E0034F49C /* SuccessCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 30C544AF167E4D9E0034F49C /* SuccessCommand.h */; }; 30C544B2167E4D9E0034F49C /* SuccessCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 30C544AF167E4D9E0034F49C /* SuccessCommand.h */; }; 30C544B3167E4D9E0034F49C /* SuccessCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 30C544B0167E4D9E0034F49C /* SuccessCommand.m */; }; @@ -145,9 +157,7 @@ C18A5EF1160D4AB400DC25F6 /* ImageCaptureRoute.m in Sources */ = {isa = PBXBuildFile; fileRef = C18A5EEF160D4AB400DC25F6 /* ImageCaptureRoute.m */; }; C18A5EF5160D4AD300DC25F6 /* UIView+ImageCapture.h in Headers */ = {isa = PBXBuildFile; fileRef = C18A5EF3160D4AD300DC25F6 /* UIView+ImageCapture.h */; }; C18A5EF6160D4AD300DC25F6 /* UIView+ImageCapture.m in Sources */ = {isa = PBXBuildFile; fileRef = C18A5EF4160D4AD300DC25F6 /* UIView+ImageCapture.m */; }; - C18A5F17160D528C00DC25F6 /* PublicAutomation.xcodeproj in Resources */ = {isa = PBXBuildFile; fileRef = C18A5F16160D528C00DC25F6 /* PublicAutomation.xcodeproj */; }; C18A5F1E160D547A00DC25F6 /* UIView+PublicAutomation.m in Sources */ = {isa = PBXBuildFile; fileRef = C18A5F1D160D547A00DC25F6 /* UIView+PublicAutomation.m */; }; - C18A5F21160D572300DC25F6 /* libPublicAutomation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C18A5F20160D572300DC25F6 /* libPublicAutomation.a */; }; C9605E641606BF2900170F88 /* IOSKeyboardCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = C9605E621606BF2900170F88 /* IOSKeyboardCommand.h */; }; C9605E651606BF2900170F88 /* IOSKeyboardCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = C9605E631606BF2900170F88 /* IOSKeyboardCommand.m */; }; C9605E671606BF8E00170F88 /* UIView+MapKitWorkaround.m in Sources */ = {isa = PBXBuildFile; fileRef = C9605E661606BF8E00170F88 /* UIView+MapKitWorkaround.m */; }; @@ -232,13 +242,6 @@ remoteGlobalIDString = ABA9E44015C81C2600112290; remoteInfo = CocoaHTTPServer; }; - C127EF0F1612BCD400EA121C /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = C18A5F16160D528C00DC25F6 /* PublicAutomation.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = C194253815D838BD004FC314; - remoteInfo = PublicAutomation; - }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -310,6 +313,16 @@ 30198DF917936DCD00F686A7 /* FEXTableCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEXTableCell.h; sourceTree = ""; }; 30198DFA17936DCD00F686A7 /* FEXTableCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FEXTableCell.m; sourceTree = ""; }; 30228E2916421C4400B1F9E7 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk/System/Library/Frameworks/Cocoa.framework; sourceTree = DEVELOPER_DIR; }; + 3023E0F21867D60500E2EA70 /* KIFTypist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = KIFTypist.h; path = KIF/Classes/KIFTypist.h; sourceTree = ""; }; + 3023E0F31867D60500E2EA70 /* KIFTypist.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = KIFTypist.m; path = KIF/Classes/KIFTypist.m; sourceTree = ""; }; + 3023E0F61867D62200E2EA70 /* CGGeometry-KIFAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "CGGeometry-KIFAdditions.h"; path = "KIF/Additions/CGGeometry-KIFAdditions.h"; sourceTree = ""; }; + 3023E0F71867D62200E2EA70 /* CGGeometry-KIFAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "CGGeometry-KIFAdditions.m"; path = "KIF/Additions/CGGeometry-KIFAdditions.m"; sourceTree = ""; }; + 3023E0F81867D62200E2EA70 /* UIAccessibilityElement-KIFAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIAccessibilityElement-KIFAdditions.h"; path = "KIF/Additions/UIAccessibilityElement-KIFAdditions.h"; sourceTree = ""; }; + 3023E0F91867D62200E2EA70 /* UIAccessibilityElement-KIFAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIAccessibilityElement-KIFAdditions.m"; path = "KIF/Additions/UIAccessibilityElement-KIFAdditions.m"; sourceTree = ""; }; + 3023E0FA1867D62200E2EA70 /* UIApplication-KIFAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIApplication-KIFAdditions.h"; path = "KIF/Additions/UIApplication-KIFAdditions.h"; sourceTree = ""; }; + 3023E0FB1867D62200E2EA70 /* UIApplication-KIFAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIApplication-KIFAdditions.m"; path = "KIF/Additions/UIApplication-KIFAdditions.m"; sourceTree = ""; }; + 3023E0FC1867D62200E2EA70 /* UIView-KIFAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIView-KIFAdditions.h"; path = "KIF/Additions/UIView-KIFAdditions.h"; sourceTree = ""; }; + 3023E0FD1867D62200E2EA70 /* UIView-KIFAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIView-KIFAdditions.m"; path = "KIF/Additions/UIView-KIFAdditions.m"; sourceTree = ""; }; 302B803F1646DE02000F9861 /* NSImage+Frank.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSImage+Frank.h"; sourceTree = ""; }; 302B80401646DE02000F9861 /* NSImage+Frank.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSImage+Frank.m"; sourceTree = ""; }; 3034CD491794CCE000A3794F /* FEXTableRow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEXTableRow.h; sourceTree = ""; }; @@ -335,6 +348,8 @@ 30AC65A3165C39FF00DEB0AB /* FrankMac-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "FrankMac-Prefix.pch"; sourceTree = ""; }; 30AC65A5165C3A4D00DEB0AB /* OSXKeyboardCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OSXKeyboardCommand.h; sourceTree = ""; }; 30AC65A6165C3A4D00DEB0AB /* OSXKeyboardCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OSXKeyboardCommand.m; sourceTree = ""; }; + 30B7346C187EF964000136CE /* UITouch-KIFAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UITouch-KIFAdditions.h"; path = "KIF/Additions/UITouch-KIFAdditions.h"; sourceTree = ""; }; + 30B7346D187EF964000136CE /* UITouch-KIFAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UITouch-KIFAdditions.m"; path = "KIF/Additions/UITouch-KIFAdditions.m"; sourceTree = ""; }; 30C544AF167E4D9E0034F49C /* SuccessCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SuccessCommand.h; sourceTree = ""; }; 30C544B0167E4D9E0034F49C /* SuccessCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SuccessCommand.m; sourceTree = ""; }; 30E82E3017128AB500E5BC7C /* ResolutionCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResolutionCommand.h; sourceTree = ""; }; @@ -415,9 +430,7 @@ C18A5EEF160D4AB400DC25F6 /* ImageCaptureRoute.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ImageCaptureRoute.m; sourceTree = ""; }; C18A5EF3160D4AD300DC25F6 /* UIView+ImageCapture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+ImageCapture.h"; sourceTree = ""; }; C18A5EF4160D4AD300DC25F6 /* UIView+ImageCapture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+ImageCapture.m"; sourceTree = ""; }; - C18A5F16160D528C00DC25F6 /* PublicAutomation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = PublicAutomation.xcodeproj; path = PublicAutomation/PublicAutomation.xcodeproj; sourceTree = ""; }; C18A5F1D160D547A00DC25F6 /* UIView+PublicAutomation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+PublicAutomation.m"; sourceTree = ""; }; - C18A5F20160D572300DC25F6 /* libPublicAutomation.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libPublicAutomation.a; path = "lib/PublicAutomation/build/Release-iphoneos/libPublicAutomation.a"; sourceTree = ""; }; C9605E621606BF2900170F88 /* IOSKeyboardCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IOSKeyboardCommand.h; sourceTree = ""; }; C9605E631606BF2900170F88 /* IOSKeyboardCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IOSKeyboardCommand.m; sourceTree = ""; }; C9605E661606BF8E00170F88 /* UIView+MapKitWorkaround.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+MapKitWorkaround.m"; sourceTree = ""; }; @@ -513,7 +526,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - C18A5F21160D572300DC25F6 /* libPublicAutomation.a in Frameworks */, AACBBE4A0F95108600F1A2B1 /* Foundation.framework in Frameworks */, D629938011AB2DF300CE0FB0 /* UIKit.framework in Frameworks */, 00E864C5161D708B00E01209 /* MapKit.framework in Frameworks */, @@ -543,7 +555,6 @@ children = ( 30228E2916421C4400B1F9E7 /* Cocoa.framework */, 305CA7A31642063E00C4ACE5 /* Foundation.framework */, - C18A5F20160D572300DC25F6 /* libPublicAutomation.a */, 00E864C4161D708B00E01209 /* MapKit.framework */, 08FB77AEFE84172EC02AAC07 /* src */, D629926711AB2CF700CE0FB0 /* lib */, @@ -579,6 +590,25 @@ path = src; sourceTree = ""; }; + 3023E0F11867D5DC00E2EA70 /* KIF */ = { + isa = PBXGroup; + children = ( + 30B7346C187EF964000136CE /* UITouch-KIFAdditions.h */, + 30B7346D187EF964000136CE /* UITouch-KIFAdditions.m */, + 3023E0F61867D62200E2EA70 /* CGGeometry-KIFAdditions.h */, + 3023E0F71867D62200E2EA70 /* CGGeometry-KIFAdditions.m */, + 3023E0F81867D62200E2EA70 /* UIAccessibilityElement-KIFAdditions.h */, + 3023E0F91867D62200E2EA70 /* UIAccessibilityElement-KIFAdditions.m */, + 3023E0FA1867D62200E2EA70 /* UIApplication-KIFAdditions.h */, + 3023E0FB1867D62200E2EA70 /* UIApplication-KIFAdditions.m */, + 3023E0FC1867D62200E2EA70 /* UIView-KIFAdditions.h */, + 3023E0FD1867D62200E2EA70 /* UIView-KIFAdditions.m */, + 3023E0F21867D60500E2EA70 /* KIFTypist.h */, + 3023E0F31867D60500E2EA70 /* KIFTypist.m */, + ); + name = KIF; + sourceTree = ""; + }; 305CA7751642059400C4ACE5 /* Other Frameworks */ = { isa = PBXGroup; children = ( @@ -682,14 +712,6 @@ path = Extensions; sourceTree = ""; }; - C127EF0C1612BCD400EA121C /* Products */ = { - isa = PBXGroup; - children = ( - C127EF101612BCD400EA121C /* libPublicAutomation.a */, - ); - name = Products; - sourceTree = ""; - }; C177A4C9163231870081DF77 /* AnyJSON */ = { isa = PBXGroup; children = ( @@ -816,8 +838,8 @@ D629926711AB2CF700CE0FB0 /* lib */ = { isa = PBXGroup; children = ( + 3023E0F11867D5DC00E2EA70 /* KIF */, C177A4C9163231870081DF77 /* AnyJSON */, - C18A5F16160D528C00DC25F6 /* PublicAutomation.xcodeproj */, D629926811AB2D0500CE0FB0 /* cocoahttpserver */, D67F2B3313F5F82700A0BFF1 /* LoadableCategory.h */, ); @@ -877,8 +899,10 @@ AA747D9F0F9514B9006C5449 /* Frank_Prefix.pch in Headers */, D6D05CB511C883AA0081C5A5 /* FrankCommandRoute.h in Headers */, D6D05CB711C883AA0081C5A5 /* FrankServer.h in Headers */, + 3023E1021867D62200E2EA70 /* UIApplication-KIFAdditions.h in Headers */, D6D05CB911C883AA0081C5A5 /* MapOperationCommand.h in Headers */, D6D05CBD11C883AA0081C5A5 /* RequestRouter.h in Headers */, + 3023E0F41867D60500E2EA70 /* KIFTypist.h in Headers */, D6D05CBF11C883AA0081C5A5 /* RoutingHTTPConnection.h in Headers */, D6D05CC111C883AA0081C5A5 /* StaticResourcesRoute.h in Headers */, D6D79B5D11D7FB8B003E0E60 /* Operation.h in Headers */, @@ -897,10 +921,14 @@ AB7947A915C4412300052B74 /* HTTPServer.h in Headers */, AB7947BB15C4412300052B74 /* WebSocket.h in Headers */, D6FA01B714283C4F00576AF1 /* EnginesCommand.h in Headers */, + 3023E1001867D62200E2EA70 /* UIAccessibilityElement-KIFAdditions.h in Headers */, A91F3AA615F6E456003F434F /* ExitCommand.h in Headers */, C9605E641606BF2900170F88 /* IOSKeyboardCommand.h in Headers */, C9605E6A1606BFAE00170F88 /* UIImage+Frank.h in Headers */, + 3023E1041867D62200E2EA70 /* UIView-KIFAdditions.h in Headers */, C18A5EF0160D4AB400DC25F6 /* ImageCaptureRoute.h in Headers */, + 3023E0FE1867D62200E2EA70 /* CGGeometry-KIFAdditions.h in Headers */, + 30B7346E187EF964000136CE /* UITouch-KIFAdditions.h in Headers */, C18A5EF5160D4AD300DC25F6 /* UIView+ImageCapture.h in Headers */, C177A4CC1632319A0081DF77 /* AnyJSON.h in Headers */, 9B3E52AF163FB98300EB24C2 /* LocationCommand.h in Headers */, @@ -1066,7 +1094,7 @@ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0450; + LastUpgradeCheck = 0500; }; buildConfigurationList = 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "Frank" */; compatibilityVersion = "Xcode 3.2"; @@ -1082,12 +1110,6 @@ mainGroup = 0867D691FE84028FC02AAC07 /* Frank */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; - projectReferences = ( - { - ProductGroup = C127EF0C1612BCD400EA121C /* Products */; - ProjectRef = C18A5F16160D528C00DC25F6 /* PublicAutomation.xcodeproj */; - }, - ); projectRoot = ""; targets = ( D2AAC07D0554694100DB518D /* Frank */, @@ -1102,22 +1124,11 @@ }; /* End PBXProject section */ -/* Begin PBXReferenceProxy section */ - C127EF101612BCD400EA121C /* libPublicAutomation.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libPublicAutomation.a; - remoteRef = C127EF0F1612BCD400EA121C /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; -/* End PBXReferenceProxy section */ - /* Begin PBXResourcesBuildPhase section */ D6782DD711BC67FE00FD0CE1 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - C18A5F17160D528C00DC25F6 /* PublicAutomation.xcodeproj in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1257,7 +1268,9 @@ buildActionMask = 2147483647; files = ( D6D05CB611C883AA0081C5A5 /* FrankCommandRoute.m in Sources */, + 3023E1031867D62200E2EA70 /* UIApplication-KIFAdditions.m in Sources */, D6D05CB811C883AA0081C5A5 /* FrankServer.m in Sources */, + 3023E1051867D62200E2EA70 /* UIView-KIFAdditions.m in Sources */, D6D05CBA11C883AA0081C5A5 /* MapOperationCommand.m in Sources */, D6D05CBE11C883AA0081C5A5 /* RequestRouter.m in Sources */, D6D05CC011C883AA0081C5A5 /* RoutingHTTPConnection.m in Sources */, @@ -1266,6 +1279,7 @@ 4C1DD76C12BADFE100E10B8C /* OrientationCommand.m in Sources */, 4C1DD76E12BADFE100E10B8C /* AppCommand.m in Sources */, D67F2AAE13F5E7FA00A0BFF1 /* AccessibilityCheckCommand.m in Sources */, + 3023E1011867D62200E2EA70 /* UIAccessibilityElement-KIFAdditions.m in Sources */, D67F2AC013F5F55A00A0BFF1 /* FrankLoader.m in Sources */, D6BD521F146C34BF001770B1 /* SelectorEngineRegistry.m in Sources */, 0071264714F8956700E738ED /* ViewJSONSerializer.m in Sources */, @@ -1276,7 +1290,10 @@ C9605E651606BF2900170F88 /* IOSKeyboardCommand.m in Sources */, 0658EF1F17E52FBB000614E6 /* UIApplication+FrankAutomation.m in Sources */, C9605E671606BF8E00170F88 /* UIView+MapKitWorkaround.m in Sources */, + 3023E0FF1867D62200E2EA70 /* CGGeometry-KIFAdditions.m in Sources */, + 30B7346F187EF964000136CE /* UITouch-KIFAdditions.m in Sources */, C9605E6B1606BFAE00170F88 /* UIImage+Frank.m in Sources */, + 3023E0F51867D60500E2EA70 /* KIFTypist.m in Sources */, C18A5EF1160D4AB400DC25F6 /* ImageCaptureRoute.m in Sources */, C18A5EF6160D4AD300DC25F6 /* UIView+ImageCapture.m in Sources */, C18A5F1E160D547A00DC25F6 /* UIView+PublicAutomation.m in Sources */, @@ -1348,10 +1365,7 @@ GCC_PREFIX_HEADER = src/Frank_Prefix.pch; INSTALL_PATH = /usr/local/lib; IPHONEOS_DEPLOYMENT_TARGET = 5.1.1; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "\"$(SRCROOT)/lib/PublicAutomation/build/$(CONFIGURATION)-$(PLATFORM_NAME)\"", - ); + LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = ( "-ObjC", "-all_load", @@ -1372,10 +1386,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = src/Frank_Prefix.pch; INSTALL_PATH = /usr/local/lib; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "\"$(SRCROOT)/lib/PublicAutomation/build/$(CONFIGURATION)-$(PLATFORM_NAME)\"", - ); + LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = ( "-ObjC", "-all_load", diff --git a/Frank.xcodeproj/xcshareddata/xcschemes/CocoaHTTPServer.xcscheme b/Frank.xcodeproj/xcshareddata/xcschemes/CocoaHTTPServer.xcscheme index 90d0397..312644a 100644 --- a/Frank.xcodeproj/xcshareddata/xcschemes/CocoaHTTPServer.xcscheme +++ b/Frank.xcodeproj/xcshareddata/xcschemes/CocoaHTTPServer.xcscheme @@ -1,6 +1,6 @@ = 9) + { + // iOS9 is installed. The UIApplicationDidBecomeActiveNotification may have been fired *before* + // this code is called. + // See also: + // http://stackoverflow.com/questions/31785878/ios-9-uiapplicationdidbecomeactivenotification-callback-not-called + + // Call applicationDidBecomeActive: after 0.5 second. + // Delay execution of my block for 10 seconds. + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 500 * USEC_PER_SEC), dispatch_get_main_queue(), ^{ + NSLog(@"Forcefully invoking applicationDidBecomeActive"); + [FrankLoader applicationDidBecomeActive:nil]; + }); + } +#endif } @end diff --git a/src/IOSKeyboardCommand.m b/src/IOSKeyboardCommand.m index 1cf989b..37edce3 100644 --- a/src/IOSKeyboardCommand.m +++ b/src/IOSKeyboardCommand.m @@ -4,12 +4,11 @@ // To change the template use AppCode | Preferences | File Templates. // - +#import "FranklyProtocolHelper.h" #import "IOSKeyboardCommand.h" #import "JSON.h" -#import - -#import "FranklyProtocolHelper.h" +#import "KIFTypist.h" +#import "UIApplication-KIFAdditions.h" @implementation IOSKeyboardCommand { @@ -25,11 +24,14 @@ - (NSString *)handleCommandWithRequestBody:(NSString *)requestBody { NSDictionary *requestCommand = FROM_JSON(requestBody); NSString *textToType = [requestCommand objectForKey:@"text_to_type"]; - if( ![UIAutomationBridge checkForKeyboard] ){ + if( ![[UIApplication sharedApplication] keyboardWindow]){ return [self generateKeyboardNotPresentErrorResponse]; } - [UIAutomationBridge typeIntoKeyboard:textToType]; + for (NSUInteger characterIndex = 0; characterIndex < [textToType length]; characterIndex++) { + NSString *characterString = [textToType substringWithRange:NSMakeRange(characterIndex, 1)]; + [KIFTypist enterCharacter: characterString]; + } return [FranklyProtocolHelper generateSuccessResponseWithoutResults]; } diff --git a/src/LocationCommand.m b/src/LocationCommand.m index ea1d8f3..6ac000c 100644 --- a/src/LocationCommand.m +++ b/src/LocationCommand.m @@ -6,15 +6,39 @@ // // +#import + #import "LocationCommand.h" -#import #import "FranklyProtocolHelper.h" #import "JSON.h" +@interface NSObject () +- (id)initWithLatitude: (double) latitude longitude: (double) longitude; + +- (id) location; + +- (void) locationManager: (id) locationManager didUpdateLocations: (NSArray*) locations; +- (void) locationManager: (id) locationManager didUpdateToLocation: (id) newLocation fromLocation: (id) oldLocation; +@end + +static LocationCommand* FEX_staticLocationCommand = nil; +static id FEX_overridenLocation = nil; +static NSMutableDictionary* FEX_locationDelegates = nil; +static NSMutableDictionary* FEX_updatingManagers = nil; @implementation LocationCommand +- (id) init +{ + if (self = [super init]) + { + FEX_staticLocationCommand = self; + } + + return self; +} + - (NSString *)handlePost:(NSString *)requestBody{ NSDictionary *requestCommand = FROM_JSON(requestBody); @@ -27,7 +51,29 @@ - (NSString *)handlePost:(NSString *)requestBody{ NSLog(@"simulating location of %f,%f",locationAsPoint.x, locationAsPoint.y); - [UIAutomationBridge setLocation:locationAsPoint]; + FEX_overridenLocation = [[NSClassFromString(@"CLLocation") alloc] initWithLatitude: locationAsPoint.x + longitude: locationAsPoint.y]; + + for (id locationManagerDescription in FEX_locationDelegates) + { + id locationManager =[FEX_updatingManagers objectForKey: locationManagerDescription]; + if (locationManager != nil) + { + id delegate = [FEX_locationDelegates objectForKey: [locationManager description]]; + if ([delegate respondsToSelector: @selector(locationManager:didUpdateLocations:)]) + { + [delegate locationManager: locationManager + didUpdateLocations: [NSArray arrayWithObject: [((NSObject*) locationManager) location]]]; + } + + if ([delegate respondsToSelector: @selector(locationManager:didUpdateToLocation:fromLocation:)]) + { + [delegate locationManager: locationManager + didUpdateToLocation: [((NSObject*) locationManager) location] + fromLocation: [((NSObject*) locationManager) location]]; + } + } + } return [FranklyProtocolHelper generateSuccessResponseWithoutResults]; } @@ -40,4 +86,124 @@ - (NSString *)handleCommandWithRequestBody:(NSString *)requestBody { return [self handlePost:requestBody]; } +- (id) FEX_location +{ + id returnValue = nil; + + if (FEX_overridenLocation != nil) + { + returnValue = FEX_overridenLocation; + } + else + { + returnValue = [self FEX_location]; + } + + return returnValue; +} + +- (void) FEX_setDelegate: (id) delegate +{ + [FEX_locationDelegates setObject: delegate forKey: [self description]]; + [self FEX_setDelegate: FEX_staticLocationCommand]; +} + +- (void) FEX_startUpdatingLocation +{ + [FEX_updatingManagers setObject: self forKey: [self description]]; + [self FEX_startUpdatingLocation]; +} + +- (void) FEX_stopUpdatingLocation +{ + [FEX_updatingManagers removeObjectForKey: [self description]]; + [self FEX_stopUpdatingLocation]; +} + +- (void) FEX_dealloc +{ + [FEX_locationDelegates removeObjectForKey: [self description]]; + [FEX_updatingManagers removeObjectForKey: [self description]]; + + [self FEX_dealloc]; +} + ++ (void) insertMethod: (SEL) newSelector andSwapWithMethod: (SEL) origSelector +{ + Class locationManagerClass = NSClassFromString(@"CLLocationManager"); + + if (locationManagerClass != nil) + { + Method origMethod = class_getInstanceMethod(locationManagerClass, origSelector); + Method newMethod = class_getInstanceMethod(self, newSelector); + IMP newIMP = method_getImplementation(newMethod); + + if (class_addMethod(locationManagerClass, newSelector, newIMP, method_getTypeEncoding(newMethod))) + { + newMethod = class_getInstanceMethod(locationManagerClass, newSelector); + + if (origMethod != NULL && newMethod != NULL) + { + method_exchangeImplementations(origMethod, newMethod); + } + } + } +} + ++ (void) load +{ + FEX_locationDelegates = [NSMutableDictionary new]; + FEX_updatingManagers = [NSMutableDictionary new]; + + [self insertMethod: @selector(FEX_setDelegate:) andSwapWithMethod: @selector(setDelegate:)]; + [self insertMethod: @selector(FEX_dealloc) andSwapWithMethod: @selector(dealloc)]; + [self insertMethod: @selector(FEX_startUpdatingLocation) andSwapWithMethod: @selector(startUpdatingLocation)]; + [self insertMethod: @selector(FEX_stopUpdatingLocation) andSwapWithMethod: @selector(stopUpdatingLocation)]; + [self insertMethod: @selector(FEX_location) andSwapWithMethod: @selector(location)]; +} + +- (void) locationManager: (id) locationManager didUpdateLocations: (NSArray*) locations +{ + if (locationManager != nil && [FEX_updatingManagers objectForKey: [locationManager description]] != nil) + { + id delegate = [FEX_locationDelegates objectForKey: [locationManager description]]; + if ([delegate respondsToSelector: @selector(locationManager:didUpdateLocations:)]) + { + if (FEX_overridenLocation == nil) + { + [delegate locationManager: locationManager + didUpdateLocations: locations]; + } + else + { + [delegate locationManager: locationManager + didUpdateLocations: [NSArray arrayWithObject: FEX_overridenLocation]]; + } + } + } +} + +- (void) locationManager: (id) locationManager didUpdateToLocation: (id) newLocation fromLocation: (id) oldLocation +{ + if (locationManager != nil && [FEX_updatingManagers objectForKey: [locationManager description]] != nil) + { + id delegate = [FEX_locationDelegates objectForKey: [locationManager description]]; + if ([delegate respondsToSelector: @selector(locationManager:didUpdateToLocation:fromLocation:)]) + { + if (FEX_overridenLocation == nil) + { + [delegate locationManager: locationManager + didUpdateToLocation: newLocation + fromLocation: oldLocation]; + } + else + { + [delegate locationManager: locationManager + didUpdateToLocation: FEX_overridenLocation + fromLocation: FEX_overridenLocation]; + } + } + } +} + @end \ No newline at end of file diff --git a/src/MapOperationCommand.m b/src/MapOperationCommand.m index eadd139..27b5de9 100644 --- a/src/MapOperationCommand.m +++ b/src/MapOperationCommand.m @@ -58,7 +58,15 @@ - (NSString *)handleCommandWithRequestBody:(NSString *)requestBody { for (FrankMapViewType *view in viewsToMap) { @try { id result = [self performOperation:operation onView:view]; - [results addObject:[ViewJSONSerializer jsonify:result]]; + NSString *resultValue = [ViewJSONSerializer jsonify:result]; + + // Can't add nil objects to an array. + if(resultValue == nil) + { + resultValue = @""; + } + + [results addObject:resultValue]; } @catch (NSException * e) { NSLog( @"Exception while performing operation %@\n%@", operation, e ); diff --git a/src/OrientationCommand.m b/src/OrientationCommand.m index f6861e8..df5128b 100644 --- a/src/OrientationCommand.m +++ b/src/OrientationCommand.m @@ -7,11 +7,12 @@ // #import "OrientationCommand.h" - -#import #import "FranklyProtocolHelper.h" #import "JSON.h" +@interface UIApplication (Private) +- (BOOL)rotateIfNeeded:(UIDeviceOrientation)orientation; +@end @implementation OrientationCommand @@ -106,7 +107,7 @@ - (NSString *)handlePost:(NSString *)requestBody{ andDetails:[NSString stringWithFormat:@"orientation '%@' is invalid. Use 'landscape_right','landscape_left','portrait', or 'portrait_upside_down'", requestBody]]; } - [UIAutomationBridge setOrientation:requestedOrientation]; + [[UIApplication sharedApplication] rotateIfNeeded:requestedOrientation]; return [FranklyProtocolHelper generateSuccessResponseWithoutResults]; } diff --git a/src/UISlider+PublicAutomation.m b/src/UISlider+PublicAutomation.m index a536f75..701d4d4 100644 --- a/src/UISlider+PublicAutomation.m +++ b/src/UISlider+PublicAutomation.m @@ -3,17 +3,42 @@ // // Created by Alvaro Barbeira on 27/3/13 // -#import #import "LoadableCategory.h" +#import "CGGeometry-KIFAdditions.h" +#import "UIApplication-KIFAdditions.h" + +@interface UIView () + +- (BOOL) FEX_dragFromPoint: (CGPoint) startPoint + toPoint: (CGPoint) destPoint + duration: (CGFloat) duration + delay: (BOOL) delay; + +@end + MAKE_CATEGORIES_LOADABLE(UISlider_PublicAutomation) +#define UI_SLIDER_TIME_STEP 0.1 + @implementation UISlider(PublicAutomation) -- (BOOL) FEX_dragThumbToValue:(double)value withDuration:(NSTimeInterval)interval { - return [UIAutomationBridge dragThumbInSlider:self toValue:value withDuration:interval]; +- (BOOL) FEX_dragThumbToValue: (double) value withDuration: (NSTimeInterval) duration +{ + CGRect bounds = [self bounds]; + CGRect trackRect = [self trackRectForBounds: bounds]; + CGRect startRect = [self thumbRectForBounds: bounds trackRect: trackRect value: [self value]]; + + CGPoint startPoint = CGPointCenteredInRect(startRect); + CGPoint destPoint = CGPointMake(bounds.size.width * value / [self maximumValue], 1.0); + + startPoint = [self convertPoint: startPoint toView: nil]; + destPoint = [self convertPoint: destPoint toView: nil]; + + return [self FEX_dragFromPoint: startPoint toPoint: destPoint duration: duration delay: NO]; } - (BOOL) FEX_dragThumbToValue:(double) value { - return [UIAutomationBridge dragThumbInSlider:self toValue:value]; + return [self FEX_dragThumbToValue: value withDuration: UI_SLIDER_TIME_STEP]; } + @end \ No newline at end of file diff --git a/src/UIView+PublicAutomation.m b/src/UIView+PublicAutomation.m index 04c5d6b..261b1dd 100644 --- a/src/UIView+PublicAutomation.m +++ b/src/UIView+PublicAutomation.m @@ -6,15 +6,18 @@ // Copyright (c) 2011 ThoughtWorks. All rights reserved. // -#import - #import "LoadableCategory.h" +#import "CGGeometry-KIFAdditions.h" +#import "UIApplication-KIFAdditions.h" +#import "UITouch-KIFAdditions.h" +#import "UIView-KIFAdditions.h" MAKE_CATEGORIES_LOADABLE(UIView_PublicAutomation) -NSString * formatCGPointVal(NSValue *val) { - CGPoint p = [val CGPointValue]; - return [NSString stringWithFormat:@"[%.2f, %.2f]", p.x, p.y]; -} +@interface UIView () + +- (UIEvent*) _eventWithTouch: (UITouch*) touch; + +@end @implementation UIView(PublicAutomation) @@ -160,7 +163,7 @@ - (BOOL)FEX_touchPoint:(CGPoint)point { return NO; } - [UIAutomationBridge tapView:self atPoint:point]; + [self tapAtPoint: point]; return YES; } @@ -175,7 +178,7 @@ - (BOOL)FEX_forcedTouch { return NO; } - [UIAutomationBridge tapView:self atPoint:point]; + [self tapAtPoint: point]; return YES; } @@ -194,7 +197,7 @@ - (BOOL)FEX_forcedTouchx:(NSNumber *)x y:(NSNumber *)y { return NO; } - [UIAutomationBridge tapView:self atPoint:point]; + [self tapAtPoint: point]; return YES; } @@ -209,7 +212,8 @@ - (BOOL)doubleTapPoint:(CGPoint)point { return NO; } - [UIAutomationBridge doubleTapView:self atPoint:point]; + [self tapAtPoint: point]; + [self tapAtPoint: point]; return YES; } @@ -242,7 +246,7 @@ - (BOOL)touchAndHold:(NSTimeInterval)duration point:(CGPoint)point { return NO; } - [UIAutomationBridge longTapView:self atPoint:point forDuration:duration]; + [self longPressAtPoint: point duration: duration]; return YES; } @@ -273,20 +277,135 @@ - (BOOL)touchAndHold:(CGFloat)duration x:(NSNumber *)x y:(NSNumber *)y { //TODO //-(void)swipeInDirection:(NSString *)dir by:(int)pixels { +// THESE MAGIC NUMBERS ARE IMPORTANT. From experimentation it appears that too big or too small a ration leads to +// gestures not being recognized as such by the system. For example setting the big ratio to 0.4 leads to +// swipe-to-delete not working on UITableViewCells. +// Also note that we always include at least a small component in each axes because in the past totally 'right-angled' +//swipes weren't detected properly. But we were using a different approach to touch simulation then, +//so this might now be unnecessary. +#define BIG_RATIO (0.3) +#define BIG_RATIO_IOS7 (0.4) +#define SMALL_RATIO (0.05) +#define SWIPE_DURATION (0.1) + +//returns what portion of the view to swipe along in the x and y axes. ++ (CGSize) swipeRatiosForDirection: (NSString*) direction +{ + CGFloat bigRatio = BIG_RATIO; + + if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) + { + bigRatio = BIG_RATIO_IOS7; + } + + if ([direction isEqualToString: @"left"]) + { + return CGSizeMake(-bigRatio, SMALL_RATIO); + } + else if ([direction isEqualToString: @"right"]) + { + return CGSizeMake(bigRatio, SMALL_RATIO); + } + else if ([direction isEqualToString: @"up"]) + { + return CGSizeMake(SMALL_RATIO, -bigRatio); + } + else if ([direction isEqualToString: @"down"]) + { + return CGSizeMake(SMALL_RATIO, bigRatio); + } + else + { + [NSException raise: @"invalid swipe direction" format: @"swipe direction '%@' is invalid", direction]; + return CGSizeZero; + } +} -- (NSString *)swipeInDirection:(NSString *)strDir { - PADirection dir = [UIAutomationBridge parseDirection:strDir]; - NSArray *swipeExtremes = [UIAutomationBridge swipeView:self inDirection:dir]; - return [NSString stringWithFormat:@"%@ => %@", formatCGPointVal([swipeExtremes objectAtIndex:0]), formatCGPointVal([swipeExtremes objectAtIndex:1])]; +- (BOOL) swipeInDirection: (NSString*) strDir +{ + + CGPoint swipeStart = CGPointCenteredInRect([self accessibilityFrame]); + CGSize ratios = [[self class] swipeRatiosForDirection: strDir]; + CGSize viewSize = [self bounds].size; + CGPoint swipeEnd = CGPointMake(swipeStart.x + (ratios.width * viewSize.width), + swipeStart.y + (ratios.height * viewSize.height)); + + return [self FEX_dragToX: swipeEnd.x y: swipeEnd.y duration: SWIPE_DURATION]; } -- (BOOL)FEX_dragWithInitialDelayToX:(CGFloat)x y:(CGFloat)y { - [UIAutomationBridge dragViewWithInitialDelay:self toPoint:CGPointMake(x,y)]; - return YES; +#define DRAG_TOUCH_DELAY 0.3 +#define DRAG_STEP 0.1 + +- (BOOL)FEX_dragWithInitialDelayToX:(CGFloat)x y:(CGFloat)y +{ + return [self FEX_dragToX: x y: y duration: DRAG_TOUCH_DELAY]; +} + +- (BOOL) FEX_dragToX: (CGFloat) x y: (CGFloat) y duration: (CGFloat) duration +{ + CGPoint startPoint = CGPointCenteredInRect([self accessibilityFrame]); + return [self FEX_dragFromPoint: startPoint toPoint: CGPointMake(x, y) duration: duration delay: YES]; } -- (BOOL)FEX_dragWithInitialDelayToX:(CGFloat)x y:(CGFloat)y duration:(NSTimeInterval)duration { - [UIAutomationBridge dragViewWithInitialDelay:self toPoint:CGPointMake(x,y) duration:duration]; +- (BOOL) FEX_dragFromPoint: (CGPoint) startPoint + toPoint: (CGPoint) destPoint + duration: (CGFloat) duration + delay: (BOOL) delay +{ + CGPoint displacement = CGPointMake(destPoint.x - startPoint.x, destPoint.y - startPoint.y); + NSUInteger numSteps = duration / DRAG_STEP; + + if (numSteps < 2) + { + numSteps = 2; + } + + CGPoint *path = alloca(numSteps * sizeof(CGPoint)); + + for (NSUInteger i = 0; i < numSteps; i++) + { + CGFloat progress = ((CGFloat)i)/(numSteps - 1); + path[i] = CGPointMake(startPoint.x + (progress * displacement.x), + startPoint.y + (progress * displacement.y)); + } + + CGPoint touchPoint = [self convertPoint: startPoint fromView: nil]; + UITouch* touch = [[UITouch alloc] initAtPoint: touchPoint inView: self]; + [touch setPhase:UITouchPhaseBegan]; + + UIEvent* eventDown = [self _eventWithTouch: touch]; + [[UIApplication sharedApplication] sendEvent: eventDown]; + + CFRunLoopRunInMode(UIApplicationCurrentRunMode, delay ? DRAG_TOUCH_DELAY : 0.01, false); + + for (NSInteger pointIndex = 1; pointIndex < numSteps; ++pointIndex) + { + [touch setLocationInWindow: path[pointIndex]]; + [touch setPhase: UITouchPhaseMoved]; + + UIEvent *eventDrag = [self _eventWithTouch: touch]; + [[UIApplication sharedApplication] sendEvent: eventDrag]; + + CFRunLoopRunInMode(UIApplicationCurrentRunMode, DRAG_STEP, false); + } + + [touch setPhase: UITouchPhaseEnded]; + + UIEvent* eventUp = [self _eventWithTouch: touch]; + [[UIApplication sharedApplication] sendEvent: eventUp]; + + if (touch.view == self && [self canBecomeFirstResponder]) + { + [self becomeFirstResponder]; + } + + while (UIApplicationCurrentRunMode != kCFRunLoopDefaultMode) + { + CFRunLoopRunInMode(UIApplicationCurrentRunMode, 0.1, false); + } + + [touch release]; + return YES; }