From 562a5dc071db643247ac72734e757f8d7cb0f2d2 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 25 Jan 2022 10:17:16 +0100 Subject: [PATCH 01/18] Add app icon, update info.plist file --- .../AppIcon.appiconset/Contents.json | 18 ++++++++ .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 909 bytes .../Icon-App-20x20@2x-1.png | Bin 0 -> 1706 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 1706 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 2303 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 1247 bytes .../Icon-App-29x29@2x-1.png | Bin 0 -> 2260 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 2260 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 3370 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 1706 bytes .../Icon-App-40x40@2x-1.png | Bin 0 -> 3211 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 3211 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 4313 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 4313 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 6262 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 2990 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 5312 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 6074 bytes .../AppIcon.appiconset/ItunesArtwork@2x.png | Bin 0 -> 39006 bytes Example/DApp/Info.plist | 39 ------------------ Example/ExampleApp.xcodeproj/project.pbxproj | 20 +++++---- 21 files changed, 30 insertions(+), 47 deletions(-) create mode 100644 Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x-1.png create mode 100644 Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-1.png create mode 100644 Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-1.png create mode 100644 Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 Example/DApp/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png diff --git a/Example/DApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/Example/DApp/Assets.xcassets/AppIcon.appiconset/Contents.json index 9221b9bb1..78d34c2c3 100644 --- a/Example/DApp/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/Example/DApp/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,91 +1,109 @@ { "images" : [ { + "filename" : "Icon-App-20x20@2x.png", "idiom" : "iphone", "scale" : "2x", "size" : "20x20" }, { + "filename" : "Icon-App-20x20@3x.png", "idiom" : "iphone", "scale" : "3x", "size" : "20x20" }, { + "filename" : "Icon-App-29x29@2x.png", "idiom" : "iphone", "scale" : "2x", "size" : "29x29" }, { + "filename" : "Icon-App-29x29@3x.png", "idiom" : "iphone", "scale" : "3x", "size" : "29x29" }, { + "filename" : "Icon-App-40x40@2x.png", "idiom" : "iphone", "scale" : "2x", "size" : "40x40" }, { + "filename" : "Icon-App-40x40@3x.png", "idiom" : "iphone", "scale" : "3x", "size" : "40x40" }, { + "filename" : "Icon-App-60x60@2x.png", "idiom" : "iphone", "scale" : "2x", "size" : "60x60" }, { + "filename" : "Icon-App-60x60@3x.png", "idiom" : "iphone", "scale" : "3x", "size" : "60x60" }, { + "filename" : "Icon-App-20x20@1x.png", "idiom" : "ipad", "scale" : "1x", "size" : "20x20" }, { + "filename" : "Icon-App-20x20@2x-1.png", "idiom" : "ipad", "scale" : "2x", "size" : "20x20" }, { + "filename" : "Icon-App-29x29@1x.png", "idiom" : "ipad", "scale" : "1x", "size" : "29x29" }, { + "filename" : "Icon-App-29x29@2x-1.png", "idiom" : "ipad", "scale" : "2x", "size" : "29x29" }, { + "filename" : "Icon-App-40x40@1x.png", "idiom" : "ipad", "scale" : "1x", "size" : "40x40" }, { + "filename" : "Icon-App-40x40@2x-1.png", "idiom" : "ipad", "scale" : "2x", "size" : "40x40" }, { + "filename" : "Icon-App-76x76@1x.png", "idiom" : "ipad", "scale" : "1x", "size" : "76x76" }, { + "filename" : "Icon-App-76x76@2x.png", "idiom" : "ipad", "scale" : "2x", "size" : "76x76" }, { + "filename" : "Icon-App-83.5x83.5@2x.png", "idiom" : "ipad", "scale" : "2x", "size" : "83.5x83.5" }, { + "filename" : "ItunesArtwork@2x.png", "idiom" : "ios-marketing", "scale" : "1x", "size" : "1024x1024" diff --git a/Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..e8b3b928c6cbe31cb3bd7f8e321c0430cfab8603 GIT binary patch literal 909 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 A0ssI2 literal 0 HcmV?d00001 diff --git a/Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..7f3a702c374a8efe35d793c31a205cffb1b42ecf GIT binary patch literal 1706 zcmV;b237fqP)4Tx0C=2ZU|>>7EGWofVPIg$%_}Jia(7aQh>TKTf5^ZNguD!53<`H2BL+u3tZkNpBf}F%kg#cp$t|bGMq*j!GXy^Qb%A(Blj1mP$U?`<3c;+SR z=_nW(7@9LMfWjQ2`g0&SEE&blAjF#QGcefh|NsC0ZiJZEeg=k(K%HDW5n>t^3=D$Y z3=AiB79!vFuj7ybYLcQH`&9R`L2c>on5W$tWJ|6>3E1rJF?K~#90?U-ALl~ojgzrD|# zxtrrSHboD4874&vi%_EnNmDVEf*^`OdWaycL;~%GgGLu261x~a1Yt-KM0v~W7(!l) z90VO3#u+uKM(3vEoS8Z2?7eze|3Bx9GpD&6p@LZtKj+_$4CDi7n+2>HNhNUtku;0l zNobBiXIxbkNgAwMVcoT19FJSYMfHPji09~*?j7lgWIp{)zzPRHd&U|a!ImKo+u zG(0ldP?Vbr1v3oLA$!nEu$92KXN0Y%JQv!uEk6gMkyO7INI3PZ7nnE>mfdf8a=PiK z>y`jYu ze{{g=y^hmOP*$MZb*%?-EL4=jJrfM&MLM>r6MnlY)VD&?!`M7MBrRQVZ#isO7~!tl z0)Rmv2aQ255V7#(DbMlWVM?ib>_!~sOfoE+VVH5JA#zhjbslQ|6t>m~hZ}|BTqrMw zqnBWNt!Kq-i}QmYoQxh63DQWAB3fdwW}oN8WuYhvs~)yIeP2qu6NLg@P`cr-PYWB5 zcskkc`&-mULRH!ySvDk8G2#>=)E-=}RLm{8aCZne?ani(>u zuQ%1z4Xvkd`K#*Dd|xSK-5-bdTfC^4tTax6hhX)$u_H zw0FbGU7n+tgr6@vHZ6`YxwQZ3?v24P5EBlb`pR*!9mA_(9E zfX{ysHXZYfD^MM6UGU07mggTdy^AuW=C*dh%R4=1t_Tx~pt%$7E`e{Ji4cthU;$6> z*)xF$qZXc>VX+3f5?T??UJ;h>a5Tr%fb%*wVqwck&!%IZlKfzm0wwwI(J{}Klb(px zvEhkrj=}OBj#-^W_ey2Ee@%OdD`a_<=s8)&gE~pp}pDc|~UaVs` zpYVKkLJb)MH+r^|rD^tdp-P?a{vG=%{CP$!fXDr8FxR?Idmo@!aO z+tJXfaB&CS30ODJvSey1UT>C+c`%%?w@!HLpeHY)p6pD(ZF!ogqgE?POAO{$7(Q5J zF~F;PJO}C(KItLhp*R<=_h@-cIOuWk_M?_Xm6_rG5fD^)j)epD!W;WN(Eu{xG@c>> z+@eaudkbuz%fjpXJ$vdD(s?}~;oO9ScNbXZSD0QKnPkvf94a`}AiTEElVjC$#wgs9 zsfKsvTaBHxE42u`dBC&loI;AICzBrDUuc;-d2qNP0j5J9ZWPw+^H`%>{P;A(`gth} zVSuM=TwCqgUaPg(3v5_unKNlt=H{dv zx#;`o8j1|-${$`d|LTD9F{#{{snCag0yK5N9b<-fS%ndNlML5DwsVL5U2IL?7#pg{ z2p2Y@5BK4G^kg#!{s;doi~?pWqk!4UC}6hoH~csBNPfMn>Hq)$07*qoM6N<$f}02$ A0ssI2 literal 0 HcmV?d00001 diff --git a/Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..d38db763f8541059b36d84eb9c4844a1a9ca89da GIT binary patch literal 2303 zcmV4Tx0C=2ZU|>>7EGWofVPIg$%_}Jia(7aQh>TKTf5^ZNguD!53<`H2BL+u3tZkNpBf}F%kg#cp$t|bGMq*j!GXy^Qb%A(Blj1mP$U?`<3c;+SR z=_nW(7@9LMfWjQ2`g0&SEE&blAjF#QGcefh|NsC0ZiJZEeg=k(K%HDW5n>t^3=D$Y z3=AiB79!vFuj7ybYLcQH`&9R`L2c>on5W$tWJ|6>3E2V_Y^K~#90?V5jZRMi#7KlkqD zCkc%Ntr!cn6hY|^3$ZPwPQik^6?z^XdocDI$ZgxqsyI5!4Gm~WZy?ytd z&pqdU&v|cSL`3)inf-q|@LpR5xZJG*T<%r@E_bT{m%CMf%iSu#=C+ckA@bEYsJ0l!>2L^_O!5EAt^;}&P zn(JY1lVQYoI7r>V!GHWm*x4r>Iwib40&!2%)%vN6|9K~RZFK~$ zXn@wM3~Lq|KK~K@kdxA;dP#u8k?0u^b{rOtoDw{N+Ni>WAu}8Z9gll3mV}6bC07~N zE;THhYbMN5j1>*+i8?qu4qLk&&%UO^&8XI>1Q7&Zu(F(%tn=3B`wMYBcE@#wdzV`p zYtnNSV?_Z64)hNS_wRD_4?67uK{NdH zR?C8BQ*@*Y1`Z}ZdRn-r(=i-}hH3=@KU!8(bTpx9-CPfs*F!^%dOR{NoQc6;Ow+lp zO1FS>>8M97jKra_8amo6H_Ryxx**^{NRFNo?(THNJ*bI-=fg$}B%G$@ye9bae8cj2 zhWWD$m(^vCb#4@npB1`K2!A;)oEXyWP-QiP03sH~lbQ*?xZU!pD+`A%s4N9)+&>6! zjlk72H9~=NBjfO)TKN8FEnmMrv(yCFgD%=-bvubL^$WF8cyOg=OEzqma?i9dNw)uBvIo=C7OK z;TvwUYse&~$M3Bf+H-|^cQ zg)19X)PB6%;fZCv1&IW|pO)F}Qn~<$J9PZF7iRFt?z7ZT_p{_b;h}2ciX94m;W`E3U~8J?$qRo`YHS z@YGAf!~0yS_sw25@fR*);qhLV0UO--i)bslt;g|ruS^Pj6dV!h;ZD??x9C#4u!`h`<2|%yi(B=5kv5Bxrr#<`T zTP&;Qo0P8?vne$m!9e@o5bl3o_gXgeJ27ax*6_ee%M-nh-yBf~vtftFNoB*vE=Oa{ zMCid7e0Paq-Hnz_`y8FGYZ_<6dQKail^>fL?397S(EI-_-1of07`>wj-`mC#&@vNF z4{Ng@R$~#kkbn&jTD8>6k|oJKf#9_p(y=SevI>?2>X-1O0@ z!%hnr()IMffbhdVJ4E!BE$mF((_`7Nu>`c=Y`Nn)lZsT-!n3cb@YUylPI#L38L*z7 z``K45Uzj_!?y6v5NJskMo5H<29iGc{D(rO75051@joTJxMk9FL*(Yr5a@1ClQt4b{ z#XW`Fy2kRktEU&)f`K6&=|d;AF-bV6iKLY{1)Hs)Iie99%U20N7h&AfE!5FwX>FN) zWQzh0JNn{j;Rnw-#yqHvK+-P@(^1fTmB15-8d78b{5H#H<`fQF6mS^&pKl3wJ?9ur z=;N8O$9%yx9OvuVql>Zk7<1eUU^%g|2v`&+_n^;py5jJ zqg{^Sarkh9!Y;m|cvt|W1eWylZ{Haw>z?tXXTW6~FJ!Eq0UfLFOyOhYvXXt(lHI=+ z^W3SFV`W=OzddwWD*b*LD{PnS>(ckDec=8BpaNX(Rsk+|s{og~Re;OgD!}D#72tBW Z{{c7J0-wbL?PUM}002ovPDHLkV1gH9Sxx`| literal 0 HcmV?d00001 diff --git a/Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..941819fce212f20c523916380fea9450f0576883 GIT binary patch literal 1247 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 A0ssI2 literal 0 HcmV?d00001 diff --git a/Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-1.png b/Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-1.png new file mode 100644 index 0000000000000000000000000000000000000000..5c06655825fc02dee4eeb2e3e3a262f5c5f6d772 GIT binary patch literal 3211 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 literal 0 HcmV?d00001 diff --git a/Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0fdab5f9586ace7626a8e31c96c5a47240c2912c GIT binary patch literal 4313 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 literal 0 HcmV?d00001 diff --git a/Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..3b88e179c7f015f579ade8b2e29ec59cf5f9f287 GIT binary patch literal 6262 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 z zI?d@jKyE!`s;*^x-9xGzwsi6g_qvv<_QC3KY9yCOI!#G*hICepQ&>gaG0p|V1K6%=)aRzI;H>M&6U>)p&@z2&29 z_t@=^M0?bP(>E>6!e)pw!FgYKDnK=Uw`)4Crh4h_{?R2K0T4_g8Ls*!JS{v&(5~Mu zUU+&a^c=Y#l(w!(9@HB?y{I^+dquxN#-^()t|joyp!I>Nao z>H3#+s-j~mU0SVQ9`ef{QoC=?$cp9fyb!##pBED`?l&Q7>bbAp*8aN4`aulGsDA9j ztJH4+(7A>Z=~!HTKa&+?;k)}cf%|}KnGKtd!E^l|e(K0-ePjd7gM5XXbQ|}Nd^iAR LYHd<)gu?#^lZ&uu literal 0 HcmV?d00001 diff --git a/Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/Example/DApp/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..03bb3d0210da26a6704db58fcae0cce08b92ba02 GIT binary patch literal 5312 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 S63sm)kE5=vqg1b83;iDjP5l%A literal 0 HcmV?d00001 diff --git a/Example/DApp/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png b/Example/DApp/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f527fe6244244e39e804a088c941e39ce3327f07 GIT binary patch literal 39006 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 literal 0 HcmV?d00001 diff --git a/Example/DApp/Info.plist b/Example/DApp/Info.plist index 994cd9071..7c5f0be1e 100644 --- a/Example/DApp/Info.plist +++ b/Example/DApp/Info.plist @@ -2,28 +2,8 @@ - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - dApp - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - $(MARKETING_VERSION) - CFBundleVersion - $(CURRENT_PROJECT_VERSION) ITSAppUsesNonExemptEncryption - LSRequiresIPhoneOS - UIApplicationSceneManifest UIApplicationSupportsMultipleScenes @@ -41,24 +21,5 @@ - UIApplicationSupportsIndirectInputEvents - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 110eb34a4..4f7fb2daf 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -707,18 +707,20 @@ DEVELOPMENT_TEAM = W5R8AG9K22; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = DApp/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = dApp; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; - INFOPLIST_KEY_UIMainStoryboardFile = Main; + INFOPLIST_KEY_UIRequiredDeviceCapabilities = armv7; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = UIInterfaceOrientationPortrait; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.DApp; + MARKETING_VERSION = 0.0.1; + PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.dapp; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; @@ -737,18 +739,20 @@ DEVELOPMENT_TEAM = W5R8AG9K22; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = DApp/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = dApp; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; - INFOPLIST_KEY_UIMainStoryboardFile = Main; + INFOPLIST_KEY_UIRequiredDeviceCapabilities = armv7; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = UIInterfaceOrientationPortrait; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.DApp; + MARKETING_VERSION = 0.0.1; + PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.dapp; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; From 22e5fdc18b0f95b9daaca056b871bff0d912cb04 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 25 Jan 2022 12:59:31 +0100 Subject: [PATCH 02/18] add deep links --- Example/DApp/Connect/ConnectView.swift | 16 ++++++++++++++ .../DApp/Connect/ConnectViewController.swift | 7 ++++++ .../SelectChainViewController.swift | 2 ++ Example/ExampleApp.xcodeproj/project.pbxproj | 4 ++++ Example/ExampleApp/AppDelegate.swift | 22 +++++++++++++++++++ Example/ExampleApp/Info.plist | 15 ++++++++++++- Example/ExampleApp/SceneDelegate.swift | 22 ++++++++++++++----- Example/Wallet.entitlements | 10 +++++++++ 8 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 Example/Wallet.entitlements diff --git a/Example/DApp/Connect/ConnectView.swift b/Example/DApp/Connect/ConnectView.swift index ca7d7ad27..3c6d542d0 100644 --- a/Example/DApp/Connect/ConnectView.swift +++ b/Example/DApp/Connect/ConnectView.swift @@ -20,11 +20,22 @@ final class ConnectView: UIView { return button }() + let connectWalletButton: UIButton = { + let button = UIButton(type: .system) + button.setTitle("Connect Wallet", for: .normal) + button.titleLabel?.font = UIFont.systemFont(ofSize: 17.0, weight: .semibold) + button.backgroundColor = .systemBlue + button.tintColor = .white + button.layer.cornerRadius = 8 + return button + }() + override init(frame: CGRect) { super.init(frame: frame) backgroundColor = .systemBackground addSubview(qrCodeView) addSubview(copyButton) + addSubview(connectWalletButton) subviews.forEach { $0.translatesAutoresizingMaskIntoConstraints = false } @@ -38,6 +49,11 @@ final class ConnectView: UIView { copyButton.centerXAnchor.constraint(equalTo: centerXAnchor), copyButton.widthAnchor.constraint(equalTo: qrCodeView.widthAnchor), copyButton.heightAnchor.constraint(equalToConstant: 44), + + connectWalletButton.topAnchor.constraint(equalTo: copyButton.bottomAnchor, constant: 16), + connectWalletButton.centerXAnchor.constraint(equalTo: centerXAnchor), + connectWalletButton.widthAnchor.constraint(equalTo: copyButton.widthAnchor), + connectWalletButton.heightAnchor.constraint(equalToConstant: 44), ]) } diff --git a/Example/DApp/Connect/ConnectViewController.swift b/Example/DApp/Connect/ConnectViewController.swift index ce75d2e18..a63cb1d52 100644 --- a/Example/DApp/Connect/ConnectViewController.swift +++ b/Example/DApp/Connect/ConnectViewController.swift @@ -32,6 +32,8 @@ class ConnectViewController: UIViewController { } } connectView.copyButton.addTarget(self, action: #selector(copyURI), for: .touchUpInside) + connectView.connectWalletButton.addTarget(self, action: #selector(connectWithExampleWallet), for: .touchUpInside) + connectView.copyButton.isHidden = true } @@ -51,4 +53,9 @@ class ConnectViewController: UIViewController { } return nil } + + @objc func connectWithExampleWallet() { + let url = URL(string: "walletconnectwallet:\(uriString)")! + UIApplication.shared.open(url, options: [:]) { _ in } + } } diff --git a/Example/DApp/SelectChain/SelectChainViewController.swift b/Example/DApp/SelectChain/SelectChainViewController.swift index 67fa3e014..73d5f164e 100644 --- a/Example/DApp/SelectChain/SelectChainViewController.swift +++ b/Example/DApp/SelectChain/SelectChainViewController.swift @@ -28,6 +28,8 @@ class SelectChainViewController: UIViewController, UITableViewDataSource { override func loadView() { view = selectChainView + + } @objc diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 4f7fb2daf..19bf5c784 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -94,6 +94,7 @@ 84CE644A279EA1FA00142511 /* AccountRequestView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountRequestView.swift; sourceTree = ""; }; 84CE644D279ED2FF00142511 /* SelectChainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectChainView.swift; sourceTree = ""; }; 84CE6451279ED42B00142511 /* ConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectView.swift; sourceTree = ""; }; + 84CE6453279FFE1100142511 /* Wallet.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Wallet.entitlements; sourceTree = ""; }; 84F568C1279582D200D0A289 /* Signer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Signer.swift; sourceTree = ""; }; 84F568C32795832A00D0A289 /* EthereumTransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EthereumTransaction.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -155,6 +156,7 @@ 764E1D3326F8D3FC00A1FB15 = { isa = PBXGroup; children = ( + 84CE6453279FFE1100142511 /* Wallet.entitlements */, 764E1D3E26F8D3FC00A1FB15 /* ExampleApp */, 845B30ED27859686002E4094 /* ExampleAppTests */, 84CE641D27981DED00142511 /* DApp */, @@ -613,6 +615,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = Wallet.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 7; @@ -637,6 +640,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = Wallet.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 7; diff --git a/Example/ExampleApp/AppDelegate.swift b/Example/ExampleApp/AppDelegate.swift index 8476d254c..6ce1e29b9 100644 --- a/Example/ExampleApp/AppDelegate.swift +++ b/Example/ExampleApp/AppDelegate.swift @@ -15,4 +15,26 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { } + + //todo: universal links +// func application(_ application: UIApplication, +// continue userActivity: NSUserActivity, +// restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool +// { +// // Get URL components from the incoming user activity. +// guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, +// let incomingURL = userActivity.webpageURL, +// let components = NSURLComponents(url: incomingURL, resolvingAgainstBaseURL: true) else { +// return false +// } +// +// // Check for specific URL components that you need. +// guard let path = components.path, +// let params = components.queryItems else { +// return false +// } +// return true +// } + + } diff --git a/Example/ExampleApp/Info.plist b/Example/ExampleApp/Info.plist index 84b6f57d8..8f53cd324 100644 --- a/Example/ExampleApp/Info.plist +++ b/Example/ExampleApp/Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - Wallet + Demo App CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -18,6 +18,19 @@ $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString $(MARKETING_VERSION) + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + com.walletconnect.example + CFBundleURLSchemes + + walletconnectwallet + + + CFBundleVersion $(CURRENT_PROJECT_VERSION) ITSAppUsesNonExemptEncryption diff --git a/Example/ExampleApp/SceneDelegate.swift b/Example/ExampleApp/SceneDelegate.swift index 19514b446..a348cd664 100644 --- a/Example/ExampleApp/SceneDelegate.swift +++ b/Example/ExampleApp/SceneDelegate.swift @@ -10,15 +10,27 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { window?.rootViewController = UITabBarController.createExampleApp() window?.makeKeyAndVisible() } + + func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { + let url = URLContexts.first?.url + let urlString: String = url!.absoluteString + let wcUri = urlString.deletingPrefix("walletconnectwallet:") + let client = ((window!.rootViewController as! UINavigationController).viewControllers[0] as! ResponderViewController).client + try! client.pair(uri: wcUri) + } } extension UITabBarController { - static func createExampleApp() -> UITabBarController { + static func createExampleApp() -> UINavigationController { let responderController = UINavigationController(rootViewController: ResponderViewController()) - responderController.tabBarItem = UITabBarItem(title: "Wallet", image: UIImage(systemName: "dollarsign.circle"), selectedImage: nil) - let tabBarController = UITabBarController() - tabBarController.viewControllers = [responderController] - return tabBarController + return responderController + } +} + +extension String { + func deletingPrefix(_ prefix: String) -> String { + guard self.hasPrefix(prefix) else { return self } + return String(self.dropFirst(prefix.count)) } } diff --git a/Example/Wallet.entitlements b/Example/Wallet.entitlements new file mode 100644 index 000000000..44da28df4 --- /dev/null +++ b/Example/Wallet.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.developer.associated-domains + + applinks:walletconnect.com + + + From 127427a91154d2736359f7892a400c493c240114 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 25 Jan 2022 15:46:03 +0100 Subject: [PATCH 03/18] savepoint --- Example/DApp/Connect/ConnectView.swift | 24 +++++++++++++++++-- .../DApp/Connect/ConnectViewController.swift | 20 ++++++++++++++-- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/Example/DApp/Connect/ConnectView.swift b/Example/DApp/Connect/ConnectView.swift index 3c6d542d0..3bc9f1665 100644 --- a/Example/DApp/Connect/ConnectView.swift +++ b/Example/DApp/Connect/ConnectView.swift @@ -3,6 +3,14 @@ import Foundation import UIKit final class ConnectView: UIView { + var segmentedControl: UISegmentedControl { + let segmentedControl = UISegmentedControl(items: ["Pairings", "New Pairing"]) +// segmentedControl.selectedSegmentIndex = 0 + return segmentedControl + } + + let tableView = UITableView() + let qrCodeView: UIImageView = { let imageView = UIImageView() imageView.contentMode = .scaleAspectFit @@ -33,14 +41,26 @@ final class ConnectView: UIView { override init(frame: CGRect) { super.init(frame: frame) backgroundColor = .systemBackground + addSubview(segmentedControl) addSubview(qrCodeView) addSubview(copyButton) addSubview(connectWalletButton) - +// addSubview(tableView) +// tableView.register(UITableViewCell.self, forCellReuseIdentifier: "pairing_cell") subviews.forEach { $0.translatesAutoresizingMaskIntoConstraints = false } NSLayoutConstraint.activate([ - qrCodeView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 64), + + segmentedControl.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 64), + segmentedControl.centerXAnchor.constraint(equalTo: centerXAnchor), + segmentedControl.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.8), + +// tableView.topAnchor.constraint(equalTo: segmentedControl.bottomAnchor, constant: 26), +// tableView.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor), +// tableView.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor), +// tableView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor), + + qrCodeView.topAnchor.constraint(equalTo: segmentedControl.bottomAnchor, constant: 26), qrCodeView.centerXAnchor.constraint(equalTo: centerXAnchor), qrCodeView.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.6), qrCodeView.widthAnchor.constraint(equalTo: qrCodeView.heightAnchor), diff --git a/Example/DApp/Connect/ConnectViewController.swift b/Example/DApp/Connect/ConnectViewController.swift index a63cb1d52..c2866dc6a 100644 --- a/Example/DApp/Connect/ConnectViewController.swift +++ b/Example/DApp/Connect/ConnectViewController.swift @@ -1,9 +1,12 @@ import Foundation import UIKit +//import WalletConnect class ConnectViewController: UIViewController { let uriString: String +// let activePairings: [Pairing] = [] + init(uri: String) { self.uriString = uri super.init(nibName: nil, bundle: nil) @@ -33,8 +36,9 @@ class ConnectViewController: UIViewController { } connectView.copyButton.addTarget(self, action: #selector(copyURI), for: .touchUpInside) connectView.connectWalletButton.addTarget(self, action: #selector(connectWithExampleWallet), for: .touchUpInside) - +// connectView.tableView.dataSource = self connectView.copyButton.isHidden = true +// activePairings = ClientDelegate.shared.client.getSettledPairings() } @@ -56,6 +60,18 @@ class ConnectViewController: UIViewController { @objc func connectWithExampleWallet() { let url = URL(string: "walletconnectwallet:\(uriString)")! - UIApplication.shared.open(url, options: [:]) { _ in } + UIApplication.shared.open(url, options: [:]) { [weak self] _ in + self?.dismiss(animated: true, completion: nil) + } } + +// func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { +// activePairings.count +// } +// +// func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { +// let cell = tableView.dequeueReusableCell(withIdentifier: "pairing_cell", for: indexPath) +// cell.textLabel?.text = activePairings[indexPath.row].peer!.name +// return cell +// } } From ab17813ef1aa1e4c67ab4190ed674be3e9285b37 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 25 Jan 2022 16:01:51 +0100 Subject: [PATCH 04/18] savepoint --- Example/DApp/Connect/ConnectView.swift | 19 ++++++------- .../DApp/Connect/ConnectViewController.swift | 27 +++++++++---------- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/Example/DApp/Connect/ConnectView.swift b/Example/DApp/Connect/ConnectView.swift index 3bc9f1665..2e9a2e2cf 100644 --- a/Example/DApp/Connect/ConnectView.swift +++ b/Example/DApp/Connect/ConnectView.swift @@ -45,22 +45,19 @@ final class ConnectView: UIView { addSubview(qrCodeView) addSubview(copyButton) addSubview(connectWalletButton) -// addSubview(tableView) -// tableView.register(UITableViewCell.self, forCellReuseIdentifier: "pairing_cell") + addSubview(tableView) + tableView.register(UITableViewCell.self, forCellReuseIdentifier: "pairing_cell") subviews.forEach { $0.translatesAutoresizingMaskIntoConstraints = false } NSLayoutConstraint.activate([ +// + tableView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 50), + tableView.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor), + tableView.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor), + tableView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor), - segmentedControl.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 64), - segmentedControl.centerXAnchor.constraint(equalTo: centerXAnchor), - segmentedControl.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.8), - -// tableView.topAnchor.constraint(equalTo: segmentedControl.bottomAnchor, constant: 26), -// tableView.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor), -// tableView.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor), -// tableView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor), - qrCodeView.topAnchor.constraint(equalTo: segmentedControl.bottomAnchor, constant: 26), + qrCodeView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 50), qrCodeView.centerXAnchor.constraint(equalTo: centerXAnchor), qrCodeView.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.6), qrCodeView.widthAnchor.constraint(equalTo: qrCodeView.heightAnchor), diff --git a/Example/DApp/Connect/ConnectViewController.swift b/Example/DApp/Connect/ConnectViewController.swift index c2866dc6a..d47c55e37 100644 --- a/Example/DApp/Connect/ConnectViewController.swift +++ b/Example/DApp/Connect/ConnectViewController.swift @@ -1,11 +1,11 @@ import Foundation import UIKit -//import WalletConnect +import WalletConnect -class ConnectViewController: UIViewController { +class ConnectViewController: UIViewController, UITableViewDataSource { let uriString: String -// let activePairings: [Pairing] = [] + let activePairings: [Pairing] = ClientDelegate.shared.client.getSettledPairings() init(uri: String) { self.uriString = uri @@ -36,9 +36,8 @@ class ConnectViewController: UIViewController { } connectView.copyButton.addTarget(self, action: #selector(copyURI), for: .touchUpInside) connectView.connectWalletButton.addTarget(self, action: #selector(connectWithExampleWallet), for: .touchUpInside) -// connectView.tableView.dataSource = self + connectView.tableView.dataSource = self connectView.copyButton.isHidden = true -// activePairings = ClientDelegate.shared.client.getSettledPairings() } @@ -65,13 +64,13 @@ class ConnectViewController: UIViewController { } } -// func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { -// activePairings.count -// } -// -// func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { -// let cell = tableView.dequeueReusableCell(withIdentifier: "pairing_cell", for: indexPath) -// cell.textLabel?.text = activePairings[indexPath.row].peer!.name -// return cell -// } + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + activePairings.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "pairing_cell", for: indexPath) + cell.textLabel?.text = activePairings[indexPath.row].peer!.name + return cell + } } From 167c4f9307c70924a4e5a19577b576dda8b84a4d Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 26 Jan 2022 11:51:18 +0100 Subject: [PATCH 05/18] Dapp propose session on existing pairing --- Example/DApp/Connect/ConnectView.swift | 10 +---- .../DApp/Connect/ConnectViewController.swift | 42 ++++++++++++++++++- .../SelectChainViewController.swift | 2 +- Example/ExampleApp/SceneDelegate.swift | 2 +- 4 files changed, 43 insertions(+), 13 deletions(-) diff --git a/Example/DApp/Connect/ConnectView.swift b/Example/DApp/Connect/ConnectView.swift index 2e9a2e2cf..64165ff3d 100644 --- a/Example/DApp/Connect/ConnectView.swift +++ b/Example/DApp/Connect/ConnectView.swift @@ -3,12 +3,6 @@ import Foundation import UIKit final class ConnectView: UIView { - var segmentedControl: UISegmentedControl { - let segmentedControl = UISegmentedControl(items: ["Pairings", "New Pairing"]) -// segmentedControl.selectedSegmentIndex = 0 - return segmentedControl - } - let tableView = UITableView() let qrCodeView: UIImageView = { @@ -41,7 +35,6 @@ final class ConnectView: UIView { override init(frame: CGRect) { super.init(frame: frame) backgroundColor = .systemBackground - addSubview(segmentedControl) addSubview(qrCodeView) addSubview(copyButton) addSubview(connectWalletButton) @@ -50,8 +43,7 @@ final class ConnectView: UIView { subviews.forEach { $0.translatesAutoresizingMaskIntoConstraints = false } NSLayoutConstraint.activate([ -// - tableView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 50), + tableView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor), tableView.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor), tableView.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor), tableView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor), diff --git a/Example/DApp/Connect/ConnectViewController.swift b/Example/DApp/Connect/ConnectViewController.swift index d47c55e37..2b1adcf06 100644 --- a/Example/DApp/Connect/ConnectViewController.swift +++ b/Example/DApp/Connect/ConnectViewController.swift @@ -3,10 +3,11 @@ import Foundation import UIKit import WalletConnect -class ConnectViewController: UIViewController, UITableViewDataSource { +class ConnectViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { let uriString: String let activePairings: [Pairing] = ClientDelegate.shared.client.getSettledPairings() - + let segmentedControl = UISegmentedControl(items: ["Pairings", "New Pairing"]) + init(uri: String) { self.uriString = uri super.init(nibName: nil, bundle: nil) @@ -37,9 +38,24 @@ class ConnectViewController: UIViewController, UITableViewDataSource { connectView.copyButton.addTarget(self, action: #selector(copyURI), for: .touchUpInside) connectView.connectWalletButton.addTarget(self, action: #selector(connectWithExampleWallet), for: .touchUpInside) connectView.tableView.dataSource = self + connectView.tableView.delegate = self connectView.copyButton.isHidden = true + setUpSegmentedControl() } + func setUpSegmentedControl() { + segmentedControl.selectedSegmentIndex = 0 + self.navigationItem.titleView = segmentedControl + segmentedControl.addTarget(self, action: #selector(segmentAction), for: .valueChanged) + } + + @objc func segmentAction() { + if segmentedControl.selectedSegmentIndex == 0 { + connectView.tableView.isHidden = false + } else { + connectView.tableView.isHidden = true + } + } @objc func copyURI() { UIPasteboard.general.string = uriString @@ -73,4 +89,26 @@ class ConnectViewController: UIViewController, UITableViewDataSource { cell.textLabel?.text = activePairings[indexPath.row].peer!.name return cell } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let pairingTopic = activePairings[indexPath.row].topic + let permissions = Session.Permissions( + blockchains: ["eip155:1", "eip155:137"], + methods: ["eth_sendTransaction", "personal_sign", "eth_signTypedData"], + notifications: [] + ) + _ = try! ClientDelegate.shared.client.connect(sessionPermissions: permissions, topic: pairingTopic) + presentConfirmationAlert() + } + + private func presentConfirmationAlert() { + let url = URL(string: "walletconnectwallet:")! + UIApplication.shared.open(url, options: [:]) { [weak self] _ in + self?.dismiss(animated: true, completion: nil) + } + let alert = UIAlertController(title: "Session Proposal Sent", message: "You can open a wallet now", preferredStyle: .alert) + let action = UIAlertAction(title: "OK", style: .cancel) + alert.addAction(action) + present(alert, animated: true) + } } diff --git a/Example/DApp/SelectChain/SelectChainViewController.swift b/Example/DApp/SelectChain/SelectChainViewController.swift index 73d5f164e..88ea048ed 100644 --- a/Example/DApp/SelectChain/SelectChainViewController.swift +++ b/Example/DApp/SelectChain/SelectChainViewController.swift @@ -51,7 +51,7 @@ class SelectChainViewController: UIViewController, UITableViewDataSource { private func showConnectScreen(uriString: String) { DispatchQueue.main.async { [unowned self] in - let vc = ConnectViewController(uri: uriString) + let vc = UINavigationController(rootViewController: ConnectViewController(uri: uriString)) present(vc, animated: true, completion: nil) } } diff --git a/Example/ExampleApp/SceneDelegate.swift b/Example/ExampleApp/SceneDelegate.swift index a348cd664..192874800 100644 --- a/Example/ExampleApp/SceneDelegate.swift +++ b/Example/ExampleApp/SceneDelegate.swift @@ -16,7 +16,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { let urlString: String = url!.absoluteString let wcUri = urlString.deletingPrefix("walletconnectwallet:") let client = ((window!.rootViewController as! UINavigationController).viewControllers[0] as! ResponderViewController).client - try! client.pair(uri: wcUri) + try? client.pair(uri: wcUri) } } From 015a08350a4bfa03217fa8b66c8a5ecb84e08a15 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 26 Jan 2022 11:58:40 +0100 Subject: [PATCH 06/18] dapp - handle disconnect --- Example/DApp/SceneDelegate.swift | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/Example/DApp/SceneDelegate.swift b/Example/DApp/SceneDelegate.swift index 9f3943771..cec88fcbd 100644 --- a/Example/DApp/SceneDelegate.swift +++ b/Example/DApp/SceneDelegate.swift @@ -13,6 +13,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). guard let windowScene = (scene as? UIWindowScene) else { return } window = UIWindow(windowScene: windowScene) + ClientDelegate.shared.onSessionDelete = { [unowned self] in + showSelectChainScreen() + } if let session = ClientDelegate.shared.client.getSettledSessions().first { showAccountsScreen(session) } else { @@ -21,23 +24,25 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } func showSelectChainScreen() { - let vc = SelectChainViewController() - vc.onSessionSettled = { [unowned self] session in - DispatchQueue.main.async { + DispatchQueue.main.async { [unowned self] in + let vc = SelectChainViewController() + vc.onSessionSettled = { [unowned self] session in showAccountsScreen(session) } + window?.rootViewController = UINavigationController(rootViewController: vc) + window?.makeKeyAndVisible() } - window?.rootViewController = UINavigationController(rootViewController: vc) - window?.makeKeyAndVisible() } func showAccountsScreen(_ session: Session) { - let vc = AccountsViewController(session: session) - vc.onDisconnect = { [unowned self] in - showSelectChainScreen() + DispatchQueue.main.async { [unowned self] in + let vc = AccountsViewController(session: session) + vc.onDisconnect = { [unowned self] in + showSelectChainScreen() + } + window?.rootViewController = UINavigationController(rootViewController: vc) + window?.makeKeyAndVisible() } - window?.rootViewController = UINavigationController(rootViewController: vc) - window?.makeKeyAndVisible() } } From 776a6953fce0204002fe5095f4d73b9c032035a5 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 26 Jan 2022 15:43:29 +0100 Subject: [PATCH 07/18] Add public Response struct --- Sources/Relayer/WakuNetworkRelay.swift | 2 +- .../WalletConnect/Engine/PairingEngine.swift | 8 ++--- .../WalletConnect/Engine/SessionEngine.swift | 30 +++++++++++-------- .../JsonRpcHistory/JsonRpcHistory.swift | 4 +-- .../JsonRpcHistory/JsonRpcRecord.swift | 2 +- .../Relay/WalletConnectRelay.swift | 21 +++++++------ Sources/WalletConnect/Response.swift | 9 ++++++ .../WalletConnect/WalletConnectClient.swift | 2 +- .../WalletConnectClientDelegate.swift | 9 +++++- .../JSONRPC/JsonRpcResult.swift | 24 +++++++++++++++ .../WalletConnectUtils/JsonRpcHistory.swift | 2 +- .../WalletConnectUtils/JsonRpcRecord.swift | 2 +- Tests/IntegrationTests/ClientDelegate.swift | 3 ++ .../JsonRpcHistoryTests.swift | 6 ++-- .../Mocks/MockedRelay.swift | 2 +- .../PairingEngineTests.swift | 2 +- .../SessionEngineTests.swift | 9 ++++-- 17 files changed, 95 insertions(+), 42 deletions(-) create mode 100644 Sources/WalletConnect/Response.swift create mode 100644 Sources/WalletConnectUtils/JSONRPC/JsonRpcResult.swift diff --git a/Sources/Relayer/WakuNetworkRelay.swift b/Sources/Relayer/WakuNetworkRelay.swift index e126b30b9..7477a759c 100644 --- a/Sources/Relayer/WakuNetworkRelay.swift +++ b/Sources/Relayer/WakuNetworkRelay.swift @@ -184,7 +184,7 @@ public final class WakuNetworkRelay { private func acknowledgeSubscription(requestId: Int64) { let response = JSONRPCResponse(id: requestId, result: AnyCodable(true)) let responseJson = try! response.json() - _ = try? jsonRpcSubscriptionsHistory.resolve(response: JsonRpcResponseTypes.response(response)) + _ = try? jsonRpcSubscriptionsHistory.resolve(response: JsonRpcResult.response(response)) dispatcher.send(responseJson) { [weak self] error in if let error = error { self?.logger.debug("Failed to Respond for request id: \(requestId), error: \(error)") diff --git a/Sources/WalletConnect/Engine/PairingEngine.swift b/Sources/WalletConnect/Engine/PairingEngine.swift index bff5522e0..29736ef5a 100644 --- a/Sources/WalletConnect/Engine/PairingEngine.swift +++ b/Sources/WalletConnect/Engine/PairingEngine.swift @@ -196,7 +196,7 @@ final class PairingEngine { return } let response = JSONRPCResponse(id: requestId, result: AnyCodable(true)) - relayer.respond(topic: topic, response: JsonRpcResponseTypes.response(response)) { [unowned self] error in + relayer.respond(topic: topic, response: JsonRpcResult.response(response)) { [unowned self] error in if let error = error { logger.error(error) } else { @@ -209,7 +209,7 @@ final class PairingEngine { private func handlePairingPing(topic: String, requestId: Int64) { let response = JSONRPCResponse(id: requestId, result: AnyCodable(true)) - relayer.respond(topic: topic, response: JsonRpcResponseTypes.response(response)) { error in + relayer.respond(topic: topic, response: JsonRpcResult.response(response)) { error in //todo } } @@ -229,7 +229,7 @@ final class PairingEngine { try? crypto.setAgreementSecret(pairingAgreementSecret, topic: sessionProposal.topic) } let response = JSONRPCResponse(id: requestId, result: AnyCodable(true)) - relayer.respond(topic: topic, response: JsonRpcResponseTypes.response(response)) { [weak self] error in + relayer.respond(topic: topic, response: JsonRpcResult.response(response)) { [weak self] error in self?.onSessionProposal?(sessionProposal) } } @@ -260,7 +260,7 @@ final class PairingEngine { // TODO: Move JSON-RPC responding to networking layer let response = JSONRPCResponse(id: requestId, result: AnyCodable(true)) - relayer.respond(topic: proposal.topic, response: JsonRpcResponseTypes.response(response)) { [weak self] error in + relayer.respond(topic: proposal.topic, response: JsonRpcResult.response(response)) { [weak self] error in if let error = error { self?.logger.error("Could not respond with error: \(error)") } diff --git a/Sources/WalletConnect/Engine/SessionEngine.swift b/Sources/WalletConnect/Engine/SessionEngine.swift index 4f608c89b..0e1cc02b8 100644 --- a/Sources/WalletConnect/Engine/SessionEngine.swift +++ b/Sources/WalletConnect/Engine/SessionEngine.swift @@ -5,6 +5,7 @@ import WalletConnectUtils final class SessionEngine { var onSessionPayloadRequest: ((Request)->())? + var onSessionPayloadResponse: ((Response)->())? var onSessionApproved: ((Session)->())? var onApprovalAcknowledgement: ((Session) -> Void)? var onSessionRejected: ((String, SessionType.Reason)->())? @@ -184,7 +185,7 @@ final class SessionEngine { } } - func respondSessionPayload(topic: String, response: JsonRpcResponseTypes) { + func respondSessionPayload(topic: String, response: JsonRpcResult) { guard sequencesStore.hasSequence(forTopic: topic) else { logger.debug("Could not find session for topic \(topic)") return @@ -294,7 +295,7 @@ final class SessionEngine { do { try validateNotification(session: session, params: notificationParams) let response = JSONRPCResponse(id: requestId, result: AnyCodable(true)) - relayer.respond(topic: topic, response: JsonRpcResponseTypes.response(response)) { [unowned self] error in + relayer.respond(topic: topic, response: JsonRpcResult.response(response)) { [unowned self] error in if let error = error { logger.error(error) } else { @@ -332,7 +333,7 @@ final class SessionEngine { return } let response = JSONRPCResponse(id: requestId, result: AnyCodable(true)) - relayer.respond(topic: topic, response: JsonRpcResponseTypes.response(response)) { [unowned self] error in + relayer.respond(topic: topic, response: JsonRpcResult.response(response)) { [unowned self] error in if let error = error { logger.error(error) } else { @@ -362,7 +363,7 @@ final class SessionEngine { return } let response = JSONRPCResponse(id: requestId, result: AnyCodable(true)) - relayer.respond(topic: topic, response: JsonRpcResponseTypes.response(response)) { [unowned self] error in + relayer.respond(topic: topic, response: JsonRpcResult.response(response)) { [unowned self] error in if let error = error { logger.error(error) } else { @@ -419,7 +420,7 @@ final class SessionEngine { private func respond(error: WalletConnectError, requestId: Int64, topic: String) { let jsonrpcError = JSONRPCErrorResponse.Error(code: error.code, message: error.description) let response = JSONRPCErrorResponse(id: requestId, error: jsonrpcError) - relayer.respond(topic: topic, response: JsonRpcResponseTypes.error(response)) { [weak self] responseError in + relayer.respond(topic: topic, response: JsonRpcResult.error(response)) { [weak self] responseError in if let responseError = responseError { self?.logger.error("Could not respond with error: \(responseError)") } else { @@ -479,7 +480,7 @@ final class SessionEngine { methods: pendingSession.proposal.permissions.jsonrpc.methods), accounts: settledSession.settled!.state.accounts) let response = JSONRPCResponse(id: requestId, result: AnyCodable(true)) - relayer.respond(topic: topic, response: JsonRpcResponseTypes.response(response)) { [unowned self] error in + relayer.respond(topic: topic, response: JsonRpcResult.response(response)) { [unowned self] error in if let error = error { logger.error(error) } @@ -507,18 +508,21 @@ final class SessionEngine { case .pairingPayload(let payloadParams): let proposeParams = payloadParams.request.params handleProposeResponse(topic: response.topic, proposeParams: proposeParams, result: response.result) - case .sessionApprove(let approveParams): + case .sessionApprove(_): handleApproveResponse(topic: response.topic, result: response.result) + case .sessionPayload(_): + let response = Response(topic: response.topic, chainId: response.chainId, result: response.result) + onSessionPayloadResponse?(response) default: break } } - private func handleProposeResponse(topic: String, proposeParams: SessionProposal, result: Result, Error>) { + private func handleProposeResponse(topic: String, proposeParams: SessionProposal, result: JsonRpcResult) { switch result { - case .success: + case .response: break - case .failure: + case .error: wcSubscriber.removeSubscription(topic: proposeParams.topic) crypto.deletePrivateKey(for: proposeParams.proposer.publicKey) crypto.deleteAgreementSecret(for: topic) @@ -526,7 +530,7 @@ final class SessionEngine { } } - private func handleApproveResponse(topic: String, result: Result, Error>) { + private func handleApproveResponse(topic: String, result: JsonRpcResult) { guard let pendingSession = try? sequencesStore.getSequence(forTopic: topic), let settledTopic = pendingSession.pending?.outcomeTopic, @@ -535,7 +539,7 @@ final class SessionEngine { return } switch result { - case .success: + case .response: guard let settledSession = try? sequencesStore.getSequence(forTopic: settledTopic) else {return} crypto.deleteAgreementSecret(for: topic) wcSubscriber.removeSubscription(topic: topic) @@ -547,7 +551,7 @@ final class SessionEngine { blockchains: proposal.permissions.blockchain.chains, methods: proposal.permissions.jsonrpc.methods), accounts: settledSession.settled!.state.accounts) onApprovalAcknowledgement?(sessionSuccess) - case .failure: + case .error: wcSubscriber.removeSubscription(topic: topic) wcSubscriber.removeSubscription(topic: settledTopic) sequencesStore.delete(topic: topic) diff --git a/Sources/WalletConnect/JsonRpcHistory/JsonRpcHistory.swift b/Sources/WalletConnect/JsonRpcHistory/JsonRpcHistory.swift index 9909dccd2..2d0724c0f 100644 --- a/Sources/WalletConnect/JsonRpcHistory/JsonRpcHistory.swift +++ b/Sources/WalletConnect/JsonRpcHistory/JsonRpcHistory.swift @@ -6,7 +6,7 @@ protocol JsonRpcHistoryRecording { func get(id: Int64) -> JsonRpcRecord? func set(topic: String, request: WCRequest, chainId: String?) throws func delete(topic: String) - func resolve(response: JsonRpcResponseTypes) throws -> JsonRpcRecord + func resolve(response: JsonRpcResult) throws -> JsonRpcRecord func exist(id: Int64) -> Bool } //TODO -remove and use jsonrpc history only from utils @@ -40,7 +40,7 @@ class JsonRpcHistory: JsonRpcHistoryRecording { } } - func resolve(response: JsonRpcResponseTypes) throws -> JsonRpcRecord { + func resolve(response: JsonRpcResult) throws -> JsonRpcRecord { logger.debug("Resolving JSON-RPC response - ID: \(response.id)") guard var record = try? storage.get(key: "\(response.id)") else { throw WalletConnectError.internal(.noJsonRpcRequestMatchingResponse) diff --git a/Sources/WalletConnect/JsonRpcHistory/JsonRpcRecord.swift b/Sources/WalletConnect/JsonRpcHistory/JsonRpcRecord.swift index f1461c5be..da8a1e0b1 100644 --- a/Sources/WalletConnect/JsonRpcHistory/JsonRpcRecord.swift +++ b/Sources/WalletConnect/JsonRpcHistory/JsonRpcRecord.swift @@ -7,7 +7,7 @@ struct JsonRpcRecord: Codable { let id: Int64 let topic: String let request: Request - var response: JsonRpcResponseTypes? + var response: JsonRpcResult? let chainId: String? struct Request: Codable { diff --git a/Sources/WalletConnect/Relay/WalletConnectRelay.swift b/Sources/WalletConnect/Relay/WalletConnectRelay.swift index c0f39e4e0..981e682f8 100644 --- a/Sources/WalletConnect/Relay/WalletConnectRelay.swift +++ b/Sources/WalletConnect/Relay/WalletConnectRelay.swift @@ -5,9 +5,10 @@ import WalletConnectUtils struct WCResponse { let topic: String + let chainId: String? let requestMethod: WCRequest.Method let requestParams: WCRequest.Params - let result: Result, Error> + let result: JsonRpcResult } protocol WalletConnectRelaying: AnyObject { @@ -17,7 +18,7 @@ protocol WalletConnectRelaying: AnyObject { var wcRequestPublisher: AnyPublisher {get} func request(_ wcMethod: WCMethod, onTopic topic: String, completion: ((Result, JSONRPCErrorResponse>)->())?) func request(topic: String, payload: WCRequest, completion: ((Result, JSONRPCErrorResponse>)->())?) - func respond(topic: String, response: JsonRpcResponseTypes, completion: @escaping ((Error?)->())) + func respond(topic: String, response: JsonRpcResult, completion: @escaping ((Error?)->())) func subscribe(topic: String) func unsubscribe(topic: String) } @@ -48,10 +49,10 @@ class WalletConnectRelay: WalletConnectRelaying { } private let wcRequestPublisherSubject = PassthroughSubject() - private var wcResponsePublisher: AnyPublisher { + private var wcResponsePublisher: AnyPublisher { wcResponsePublisherSubject.eraseToAnyPublisher() } - private let wcResponsePublisherSubject = PassthroughSubject() + private let wcResponsePublisherSubject = PassthroughSubject() let logger: ConsoleLogging init(networkRelayer: NetworkRelaying, @@ -101,7 +102,7 @@ class WalletConnectRelay: WalletConnectRelaying { } } - func respond(topic: String, response: JsonRpcResponseTypes, completion: @escaping ((Error?)->())) { + func respond(topic: String, response: JsonRpcResult, completion: @escaping ((Error?)->())) { do { _ = try jsonRpcHistory.resolve(response: response) let message = try jsonRpcSerialiser.serialise(topic: topic, encodable: response.value) @@ -170,12 +171,13 @@ class WalletConnectRelay: WalletConnectRelaying { private func handleJsonRpcResponse(response: JSONRPCResponse) { do { - let record = try jsonRpcHistory.resolve(response: JsonRpcResponseTypes.response(response)) + let record = try jsonRpcHistory.resolve(response: JsonRpcResult.response(response)) let wcResponse = WCResponse( topic: record.topic, + chainId: record.chainId, requestMethod: record.request.method, requestParams: record.request.params, - result: .success(response)) + result: JsonRpcResult.response(response)) wcResponsePublisherSubject.send(.response(response)) onPairingResponse?(wcResponse) onResponse?(wcResponse) @@ -186,12 +188,13 @@ class WalletConnectRelay: WalletConnectRelaying { private func handleJsonRpcErrorResponse(response: JSONRPCErrorResponse) { do { - let record = try jsonRpcHistory.resolve(response: JsonRpcResponseTypes.error(response)) + let record = try jsonRpcHistory.resolve(response: JsonRpcResult.error(response)) let wcResponse = WCResponse( topic: record.topic, + chainId: record.chainId, requestMethod: record.request.method, requestParams: record.request.params, - result: .failure(response)) + result: JsonRpcResult.error(response)) wcResponsePublisherSubject.send(.error(response)) onPairingResponse?(wcResponse) onResponse?(wcResponse) diff --git a/Sources/WalletConnect/Response.swift b/Sources/WalletConnect/Response.swift new file mode 100644 index 000000000..736893173 --- /dev/null +++ b/Sources/WalletConnect/Response.swift @@ -0,0 +1,9 @@ + +import Foundation +import WalletConnectUtils + +public struct Response: Codable { + public let topic: String + public let chainId: String? + public let result: JsonRpcResult +} diff --git a/Sources/WalletConnect/WalletConnectClient.swift b/Sources/WalletConnect/WalletConnectClient.swift index 05e8188ac..e3cf0af74 100644 --- a/Sources/WalletConnect/WalletConnectClient.swift +++ b/Sources/WalletConnect/WalletConnectClient.swift @@ -180,7 +180,7 @@ public final class WalletConnectClient { /// - Parameters: /// - topic: Topic of the session for which the request was received. /// - response: Your JSON RPC response or an error. - public func respond(topic: String, response: JsonRpcResponseTypes) { + public func respond(topic: String, response: JsonRpcResult) { sessionEngine.respondSessionPayload(topic: topic, response: response) } diff --git a/Sources/WalletConnect/WalletConnectClientDelegate.swift b/Sources/WalletConnect/WalletConnectClientDelegate.swift index 372b3067a..0d2b9621b 100644 --- a/Sources/WalletConnect/WalletConnectClientDelegate.swift +++ b/Sources/WalletConnect/WalletConnectClientDelegate.swift @@ -9,13 +9,20 @@ public protocol WalletConnectClientDelegate: AnyObject { /// Function is executed on responder client only func didReceive(sessionProposal: Session.Proposal) - /// Tells the delegate that session request has been received + /// Tells the delegate that session payload request has been received /// /// In most cases that function is supposed to be called on wallet client. /// - Parameters: /// - sessionRequest: Object containing request received from peer client. func didReceive(sessionRequest: Request) + /// Tells the delegate that session payload response has been received + /// + /// In most cases that function is supposed to be called on dApp client. + /// - Parameters: + /// - sessionResponse: Object containing response received from peer client. + func didReceive(sessionResponse: Response) + /// Tells the delegate that the peer client has terminated the session. /// /// Function can be executed on any type of the client. diff --git a/Sources/WalletConnectUtils/JSONRPC/JsonRpcResult.swift b/Sources/WalletConnectUtils/JSONRPC/JsonRpcResult.swift new file mode 100644 index 000000000..f7fee74f8 --- /dev/null +++ b/Sources/WalletConnectUtils/JSONRPC/JsonRpcResult.swift @@ -0,0 +1,24 @@ + +import Foundation + + +public enum JsonRpcResult: Codable { + case error(JSONRPCErrorResponse) + case response(JSONRPCResponse) + public var id: Int64 { + switch self { + case .error(let value): + return value.id + case .response(let value): + return value.id + } + } + public var value: Codable { + switch self { + case .error(let value): + return value + case .response(let value): + return value + } + } +} diff --git a/Sources/WalletConnectUtils/JsonRpcHistory.swift b/Sources/WalletConnectUtils/JsonRpcHistory.swift index bca7270a0..8d2f27b58 100644 --- a/Sources/WalletConnectUtils/JsonRpcHistory.swift +++ b/Sources/WalletConnectUtils/JsonRpcHistory.swift @@ -35,7 +35,7 @@ public class JsonRpcHistory where T: Codable&Equatable { } } - public func resolve(response: JsonRpcResponseTypes) throws -> JsonRpcRecord { + public func resolve(response: JsonRpcResult) throws -> JsonRpcRecord { logger.debug("Resolving JSON-RPC response - ID: \(response.id)") guard var record = try? storage.get(key: "\(response.id)") else { throw RecordingError.noJsonRpcRequestMatchingResponse diff --git a/Sources/WalletConnectUtils/JsonRpcRecord.swift b/Sources/WalletConnectUtils/JsonRpcRecord.swift index 225cca1ac..598d17dfe 100644 --- a/Sources/WalletConnectUtils/JsonRpcRecord.swift +++ b/Sources/WalletConnectUtils/JsonRpcRecord.swift @@ -5,7 +5,7 @@ public struct JsonRpcRecord: Codable { public let id: Int64 public let topic: String public let request: Request - public var response: JsonRpcResponseTypes? + public var response: JsonRpcResult? public let chainId: String? public struct Request: Codable { diff --git a/Tests/IntegrationTests/ClientDelegate.swift b/Tests/IntegrationTests/ClientDelegate.swift index fa87b54cd..47f2e9098 100644 --- a/Tests/IntegrationTests/ClientDelegate.swift +++ b/Tests/IntegrationTests/ClientDelegate.swift @@ -50,4 +50,7 @@ class ClientDelegate: WalletConnectClientDelegate { func didUpdate(pairingTopic: String, appMetadata: AppMetadata) { onPairingUpdate?(pairingTopic, appMetadata) } + func didReceive(sessionResponse: Response) { + + } } diff --git a/Tests/WalletConnectTests/JsonRpcHistoryTests.swift b/Tests/WalletConnectTests/JsonRpcHistoryTests.swift index c807c1b46..34895bafb 100644 --- a/Tests/WalletConnectTests/JsonRpcHistoryTests.swift +++ b/Tests/WalletConnectTests/JsonRpcHistoryTests.swift @@ -36,7 +36,7 @@ final class JsonRpcHistoryTests: XCTestCase { try! sut.set(topic: recordinput.topic, request: recordinput.request) XCTAssertNil(sut.get(id: recordinput.request.id)?.response) let jsonRpcResponse = JSONRPCResponse(id: recordinput.request.id, result: AnyCodable("")) - let response = JsonRpcResponseTypes.response(jsonRpcResponse) + let response = JsonRpcResult.response(jsonRpcResponse) _ = try! sut.resolve(response: response) XCTAssertNotNil(sut.get(id: jsonRpcResponse.id)?.response) } @@ -45,7 +45,7 @@ final class JsonRpcHistoryTests: XCTestCase { let recordinput = getTestJsonRpcRecordInput() try! sut.set(topic: recordinput.topic, request: recordinput.request) let jsonRpcResponse = JSONRPCResponse(id: recordinput.request.id, result: AnyCodable("")) - let response = JsonRpcResponseTypes.response(jsonRpcResponse) + let response = JsonRpcResult.response(jsonRpcResponse) _ = try! sut.resolve(response: response) XCTAssertThrowsError(try sut.resolve(response: response)) } @@ -71,7 +71,7 @@ final class JsonRpcHistoryTests: XCTestCase { try! sut.set(topic: recordinput2.topic, request: recordinput2.request) XCTAssertEqual(sut.getPending().count, 2) let jsonRpcResponse = JSONRPCResponse(id: recordinput1.request.id, result: AnyCodable("")) - let response = JsonRpcResponseTypes.response(jsonRpcResponse) + let response = JsonRpcResult.response(jsonRpcResponse) _ = try! sut.resolve(response: response) XCTAssertEqual(sut.getPending().count, 1) } diff --git a/Tests/WalletConnectTests/Mocks/MockedRelay.swift b/Tests/WalletConnectTests/Mocks/MockedRelay.swift index 171f51770..55145d820 100644 --- a/Tests/WalletConnectTests/Mocks/MockedRelay.swift +++ b/Tests/WalletConnectTests/Mocks/MockedRelay.swift @@ -34,7 +34,7 @@ class MockedWCRelay: WalletConnectRelaying { requests.append((topic, payload)) } - func respond(topic: String, response: JsonRpcResponseTypes, completion: @escaping ((Error?) -> ())) { + func respond(topic: String, response: JsonRpcResult, completion: @escaping ((Error?) -> ())) { completion(error) } diff --git a/Tests/WalletConnectTests/PairingEngineTests.swift b/Tests/WalletConnectTests/PairingEngineTests.swift index 2796adcd0..8f6d51178 100644 --- a/Tests/WalletConnectTests/PairingEngineTests.swift +++ b/Tests/WalletConnectTests/PairingEngineTests.swift @@ -124,7 +124,7 @@ final class PairingEngineTests: XCTestCase { try engine.approve(uri) let success = JSONRPCResponse(id: 0, result: AnyCodable(true)) - let response = WCResponse(topic: topicA, requestMethod: .pairingApprove, requestParams: .pairingApprove(PairingType.ApprovalParams(relay: RelayProtocolOptions(protocol: "", params: nil), responder: PairingParticipant(publicKey: ""), expiry: 0, state: nil)), result: .success(success)) + let response = WCResponse(topic: topicA, chainId: nil, requestMethod: .pairingApprove, requestParams: .pairingApprove(PairingType.ApprovalParams(relay: RelayProtocolOptions(protocol: "", params: nil), responder: PairingParticipant(publicKey: ""), expiry: 0, state: nil)), result: .response(success)) relayMock.onPairingResponse?(response) XCTAssert(storageMock.hasAcknowledgedPairing(on: topicB), "Settled pairing must advance to acknowledged state.") diff --git a/Tests/WalletConnectTests/SessionEngineTests.swift b/Tests/WalletConnectTests/SessionEngineTests.swift index 5dd35b8b0..c49e20580 100644 --- a/Tests/WalletConnectTests/SessionEngineTests.swift +++ b/Tests/WalletConnectTests/SessionEngineTests.swift @@ -121,9 +121,10 @@ final class SessionEngineTests: XCTestCase { let error = JSONRPCErrorResponse(id: request.id, error: JSONRPCErrorResponse.Error(code: 0, message: "")) let response = WCResponse( topic: publishTopic, + chainId: nil, requestMethod: request.method, requestParams: request.params, - result: .failure(error)) + result: .error(error)) relayMock.onResponse?(response) XCTAssert(subscriberMock.didUnsubscribe(to: topicC)) @@ -188,9 +189,10 @@ final class SessionEngineTests: XCTestCase { let success = JSONRPCResponse(id: request.id, result: AnyCodable(true)) let response = WCResponse( topic: publishTopic, + chainId: nil, requestMethod: request.method, requestParams: request.params, - result: .success(success)) + result: .response(success)) relayMock.onResponse?(response) XCTAssertFalse(cryptoMock.hasAgreementSecret(for: topicC)) @@ -225,9 +227,10 @@ final class SessionEngineTests: XCTestCase { let error = JSONRPCErrorResponse(id: request.id, error: JSONRPCErrorResponse.Error(code: 0, message: "")) let response = WCResponse( topic: publishTopic, + chainId: nil, requestMethod: request.method, requestParams: request.params, - result: .failure(error)) + result: .error(error)) relayMock.onResponse?(response) XCTAssertFalse(cryptoMock.hasPrivateKey(for: selfPubKey)) From 2cd8bf69d0f9a42b131215f2669b0ef9d000f26c Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 27 Jan 2022 08:42:57 +0100 Subject: [PATCH 08/18] savepoint --- .../WalletConnect/Engine/SessionEngine.swift | 7 ++---- .../WalletConnect/WalletConnectClient.swift | 12 ++++++---- .../JSONRPC/JsonRpcResponseTypes.swift | 24 ------------------- 3 files changed, 9 insertions(+), 34 deletions(-) delete mode 100644 Sources/WalletConnectUtils/JSONRPC/JsonRpcResponseTypes.swift diff --git a/Sources/WalletConnect/Engine/SessionEngine.swift b/Sources/WalletConnect/Engine/SessionEngine.swift index 0e1cc02b8..46e6eaa75 100644 --- a/Sources/WalletConnect/Engine/SessionEngine.swift +++ b/Sources/WalletConnect/Engine/SessionEngine.swift @@ -3,7 +3,6 @@ import Combine import WalletConnectUtils final class SessionEngine { - var onSessionPayloadRequest: ((Request)->())? var onSessionPayloadResponse: ((Response)->())? var onSessionApproved: ((Session)->())? @@ -165,7 +164,7 @@ final class SessionEngine { } } - func request(params: Request, completion: @escaping ((Result, JSONRPCErrorResponse>)->())) { + func request(params: Request) { guard sequencesStore.hasSequence(forTopic: params.topic) else { logger.debug("Could not find session for topic \(params.topic)") return @@ -175,12 +174,10 @@ final class SessionEngine { let sessionPayloadRequest = WCRequest(id: params.id, method: .sessionPayload, params: .sessionPayload(sessionPayloadParams)) relayer.request(topic: params.topic, payload: sessionPayloadRequest) { [weak self] result in switch result { - case .success(let response): - completion(.success(response)) + case .success(_): self?.logger.debug("Did receive session payload response") case .failure(let error): self?.logger.debug("error: \(error)") - completion(.failure(error)) } } } diff --git a/Sources/WalletConnect/WalletConnectClient.swift b/Sources/WalletConnect/WalletConnectClient.swift index e3cf0af74..78b8e4749 100644 --- a/Sources/WalletConnect/WalletConnectClient.swift +++ b/Sources/WalletConnect/WalletConnectClient.swift @@ -171,9 +171,8 @@ public final class WalletConnectClient { /// For the proposer to send JSON-RPC requests to responding peer. /// - Parameters: /// - params: Parameters defining request and related session - /// - completion: completion block will provide response from responding client - public func request(params: Request, completion: @escaping (Result, JSONRPCErrorResponse>) -> ()) { - sessionEngine.request(params: params, completion: completion) + public func request(params: Request) { + sessionEngine.request(params: params) } /// For the responder to respond on pending peer's session JSON-RPC Request @@ -267,6 +266,9 @@ public final class WalletConnectClient { pairingEngine.onApprovalAcknowledgement = { [weak self] settledPairing in self?.delegate?.didSettle(pairing: settledPairing) } + pairingEngine.onPairingUpdate = { [unowned self] topic, appMetadata in + delegate?.didUpdate(pairingTopic: topic, appMetadata: appMetadata) + } sessionEngine.onSessionApproved = { [unowned self] settledSession in delegate?.didSettle(session: settledSession) } @@ -292,8 +294,8 @@ public final class WalletConnectClient { sessionEngine.onNotificationReceived = { [unowned self] topic, notification in delegate?.didReceive(notification: notification, sessionTopic: topic) } - pairingEngine.onPairingUpdate = { [unowned self] topic, appMetadata in - delegate?.didUpdate(pairingTopic: topic, appMetadata: appMetadata) + sessionEngine.onSessionPayloadResponse = { [unowned self] response in + delegate?.didReceive(sessionResponse: response) } } diff --git a/Sources/WalletConnectUtils/JSONRPC/JsonRpcResponseTypes.swift b/Sources/WalletConnectUtils/JSONRPC/JsonRpcResponseTypes.swift deleted file mode 100644 index 1ef0fb00d..000000000 --- a/Sources/WalletConnectUtils/JSONRPC/JsonRpcResponseTypes.swift +++ /dev/null @@ -1,24 +0,0 @@ - -import Foundation - - -public enum JsonRpcResponseTypes: Codable { - case error(JSONRPCErrorResponse) - case response(JSONRPCResponse) - public var id: Int64 { - switch self { - case .error(let value): - return value.id - case .response(let value): - return value.id - } - } - public var value: Codable { - switch self { - case .error(let value): - return value - case .response(let value): - return value - } - } -} From 037957da976d3484ad1c07b8e2286d0b984c1205 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 27 Jan 2022 10:13:22 +0100 Subject: [PATCH 09/18] replace session payload request completion with response delegate method in client integration tests --- .../Relay/WalletConnectRelay.swift | 2 +- Tests/IntegrationTests/ClientDelegate.swift | 3 +- Tests/IntegrationTests/ClientTest.swift | 37 ++++++++++--------- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/Sources/WalletConnect/Relay/WalletConnectRelay.swift b/Sources/WalletConnect/Relay/WalletConnectRelay.swift index d92f0f443..a4ba6928e 100644 --- a/Sources/WalletConnect/Relay/WalletConnectRelay.swift +++ b/Sources/WalletConnect/Relay/WalletConnectRelay.swift @@ -121,7 +121,7 @@ class WalletConnectRelay: WalletConnectRelaying { func respondError(for payload: WCRequestSubscriptionPayload, reason: Reason) { let response = JSONRPCErrorResponse(id: payload.wcRequest.id, error: JSONRPCErrorResponse.Error(code: reason.code, message: reason.message)) - respond(topic: payload.topic, response: JsonRpcResponseTypes.error(response)) { _ in } // TODO: Move error handling to relayer package + respond(topic: payload.topic, response: JsonRpcResult.error(response)) { _ in } // TODO: Move error handling to relayer package } func subscribe(topic: String) { diff --git a/Tests/IntegrationTests/ClientDelegate.swift b/Tests/IntegrationTests/ClientDelegate.swift index 47f2e9098..f5eacaeb0 100644 --- a/Tests/IntegrationTests/ClientDelegate.swift +++ b/Tests/IntegrationTests/ClientDelegate.swift @@ -8,6 +8,7 @@ class ClientDelegate: WalletConnectClientDelegate { var onPairingSettled: ((Pairing)->())? var onSessionProposal: ((Session.Proposal)->())? var onSessionRequest: ((Request)->())? + var onSessionResponse: ((Response)->())? var onSessionRejected: ((String, Reason)->())? var onSessionDelete: (()->())? var onSessionUpgrade: ((String, Session.Permissions)->())? @@ -51,6 +52,6 @@ class ClientDelegate: WalletConnectClientDelegate { onPairingUpdate?(pairingTopic, appMetadata) } func didReceive(sessionResponse: Response) { - + onSessionResponse?(sessionResponse) } } diff --git a/Tests/IntegrationTests/ClientTest.swift b/Tests/IntegrationTests/ClientTest.swift index cb56eff3c..4b76faf67 100644 --- a/Tests/IntegrationTests/ClientTest.swift +++ b/Tests/IntegrationTests/ClientTest.swift @@ -153,15 +153,16 @@ final class ClientTests: XCTestCase { } proposer.onSessionSettled = {[unowned self] settledSession in let requestParams = Request(id: 0, topic: settledSession.topic, method: method, params: AnyCodable(params), chainId: nil) - self.proposer.client.request(params: requestParams) { result in - switch result { - case .success(let jsonRpcResponse): - let response = try! jsonRpcResponse.result.get(String.self) - XCTAssertEqual(response, responseParams) - responseExpectation.fulfill() - case .failure(_): - XCTFail() - } + self.proposer.client.request(params: requestParams) + } + proposer.onSessionResponse = { response in + switch response.result { + case .response(let jsonRpcResponse): + let response = try! jsonRpcResponse.result.get(String.self) + XCTAssertEqual(response, responseParams) + responseExpectation.fulfill() + case .error(_): + XCTFail() } } responder.onSessionRequest = {[unowned self] sessionRequest in @@ -189,15 +190,17 @@ final class ClientTests: XCTestCase { } proposer.onSessionSettled = {[unowned self] settledSession in let requestParams = Request(id: 0, topic: settledSession.topic, method: method, params: AnyCodable(params), chainId: nil) - self.proposer.client.request(params: requestParams) { result in - switch result { - case .success(_): - XCTFail() - case .failure(let errorResponse): - XCTAssertEqual(error, errorResponse.error) - failureResponseExpectation.fulfill() - } + self.proposer.client.request(params: requestParams) + } + proposer.onSessionResponse = { response in + switch response.result { + case .response(_): + XCTFail() + case .error(let errorResponse): + XCTAssertEqual(error, errorResponse.error) + failureResponseExpectation.fulfill() } + } responder.onSessionRequest = {[unowned self] sessionRequest in let jsonrpcErrorResponse = JSONRPCErrorResponse(id: sessionRequest.id, error: error) From b35a0555d76281b3b33378ed1e23dd0649b649f7 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 27 Jan 2022 11:06:23 +0100 Subject: [PATCH 10/18] Add Response view controller fix control flow in wallet --- .../AccountRequestViewController.swift | 2 +- Example/DApp/ClientDelegate.swift | 4 + Example/DApp/ResponseViewController.swift | 107 ++++++++++++++++++ Example/DApp/SceneDelegate.swift | 9 ++ Example/ExampleApp.xcodeproj/project.pbxproj | 4 + .../Responder/ResponderViewController.swift | 7 ++ 6 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 Example/DApp/ResponseViewController.swift diff --git a/Example/DApp/AccountRequest/AccountRequestViewController.swift b/Example/DApp/AccountRequest/AccountRequestViewController.swift index f1d74c00f..5fdab0d97 100644 --- a/Example/DApp/AccountRequest/AccountRequestViewController.swift +++ b/Example/DApp/AccountRequest/AccountRequestViewController.swift @@ -59,7 +59,7 @@ class AccountRequestViewController: UIViewController, UITableViewDelegate, UITab let request = Request(topic: session.topic, method: method, params: requestParams, chainId: chainId) - client.request(params: request) { _ in } + client.request(params: request) presentConfirmationAlert() } diff --git a/Example/DApp/ClientDelegate.swift b/Example/DApp/ClientDelegate.swift index 12fa01da4..e32fcdebc 100644 --- a/Example/DApp/ClientDelegate.swift +++ b/Example/DApp/ClientDelegate.swift @@ -9,6 +9,7 @@ class ClientDelegate: WalletConnectClientDelegate { var onPairingSettled: ((Pairing)->())? var onSessionProposal: ((Session.Proposal)->())? var onSessionRequest: ((Request)->())? + var onSessionResponse: ((Response)->())? var onSessionRejected: ((String, Reason)->())? var onSessionDelete: (()->())? var onSessionUpgrade: ((String, Session.Permissions)->())? @@ -62,4 +63,7 @@ class ClientDelegate: WalletConnectClientDelegate { func didUpdate(pairingTopic: String, appMetadata: AppMetadata) { onPairingUpdate?(pairingTopic, appMetadata) } + func didReceive(sessionResponse: Response) { + onSessionResponse?(sessionResponse) + } } diff --git a/Example/DApp/ResponseViewController.swift b/Example/DApp/ResponseViewController.swift new file mode 100644 index 000000000..e53f89688 --- /dev/null +++ b/Example/DApp/ResponseViewController.swift @@ -0,0 +1,107 @@ + +import Foundation +import WalletConnect +import UIKit + +class ResponseViewController: UIViewController { + let response: Response + private let responseView = { + ResponseView() + }() + + init(response: Response) { + self.response = response + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func loadView() { + view = responseView + } + + override func viewDidLoad() { + super.viewDidLoad() + switch response.result { + case .response(let response): + responseView.nameLabel.text = "Received Response" + responseView.descriptionLabel.text = try! response.result.get(String.self).description + case .error(let error): + responseView.nameLabel.text = "Received Error" + responseView.descriptionLabel.text = error.error.message + } + responseView.dismissButton.addTarget(self, action: #selector(dismissSelf), for: .touchUpInside) + } + + @objc func dismissSelf() { + dismiss(animated: true, completion: nil) + } +} + + +final class ResponseView: UIView { + + let nameLabel: UILabel = { + let label = UILabel() + label.font = UIFont.systemFont(ofSize: 17.0, weight: .heavy) + return label + }() + + let descriptionLabel: UILabel = { + let label = UILabel() + label.font = UIFont.preferredFont(forTextStyle: .subheadline) + label.textColor = .secondaryLabel + label.numberOfLines = 0 + label.textAlignment = .center + return label + }() + + let dismissButton: UIButton = { + let button = UIButton(type: .system) + button.setTitle("Dismiss", for: .normal) + button.backgroundColor = .systemBlue + button.tintColor = .white + button.layer.cornerRadius = 8 + button.titleLabel?.font = UIFont.systemFont(ofSize: 17.0, weight: .semibold) + return button + }() + + let headerStackView: UIStackView = { + let stackView = UIStackView() + stackView.axis = .vertical + stackView.spacing = 16 + stackView.alignment = .center + return stackView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + backgroundColor = .systemBackground + + addSubview(headerStackView) + addSubview(dismissButton) + headerStackView.addArrangedSubview(nameLabel) + headerStackView.addArrangedSubview(descriptionLabel) + + subviews.forEach { $0.translatesAutoresizingMaskIntoConstraints = false } + + NSLayoutConstraint.activate([ + + headerStackView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 32), + headerStackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 32), + headerStackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -32), + + dismissButton.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -16), + dismissButton.centerXAnchor.constraint(equalTo: safeAreaLayoutGuide.centerXAnchor, constant: 16), + dismissButton.heightAnchor.constraint(equalToConstant: 44), + dismissButton.widthAnchor.constraint(equalToConstant: 120) + ]) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + diff --git a/Example/DApp/SceneDelegate.swift b/Example/DApp/SceneDelegate.swift index cec88fcbd..ba447fb33 100644 --- a/Example/DApp/SceneDelegate.swift +++ b/Example/DApp/SceneDelegate.swift @@ -16,6 +16,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { ClientDelegate.shared.onSessionDelete = { [unowned self] in showSelectChainScreen() } + ClientDelegate.shared.onSessionResponse = { [unowned self] response in + presentResponse(for: response) + } if let session = ClientDelegate.shared.client.getSettledSessions().first { showAccountsScreen(session) } else { @@ -45,5 +48,11 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } } + func presentResponse(for response: Response) { + DispatchQueue.main.async { [unowned self] in + let vc = UINavigationController(rootViewController: ResponseViewController(response: response)) + window?.rootViewController?.present(vc, animated: true, completion: nil) + } + } } diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 19bf5c784..58d30f1a7 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -41,6 +41,7 @@ 84CE644B279EA1FA00142511 /* AccountRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE644A279EA1FA00142511 /* AccountRequestView.swift */; }; 84CE644E279ED2FF00142511 /* SelectChainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE644D279ED2FF00142511 /* SelectChainView.swift */; }; 84CE6452279ED42B00142511 /* ConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE6451279ED42B00142511 /* ConnectView.swift */; }; + 84CE645527A29D4D00142511 /* ResponseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE645427A29D4C00142511 /* ResponseViewController.swift */; }; 84F568C2279582D200D0A289 /* Signer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C1279582D200D0A289 /* Signer.swift */; }; 84F568C42795832A00D0A289 /* EthereumTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C32795832A00D0A289 /* EthereumTransaction.swift */; }; /* End PBXBuildFile section */ @@ -95,6 +96,7 @@ 84CE644D279ED2FF00142511 /* SelectChainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectChainView.swift; sourceTree = ""; }; 84CE6451279ED42B00142511 /* ConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectView.swift; sourceTree = ""; }; 84CE6453279FFE1100142511 /* Wallet.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Wallet.entitlements; sourceTree = ""; }; + 84CE645427A29D4C00142511 /* ResponseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResponseViewController.swift; sourceTree = ""; }; 84F568C1279582D200D0A289 /* Signer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Signer.swift; sourceTree = ""; }; 84F568C32795832A00D0A289 /* EthereumTransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EthereumTransaction.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -231,6 +233,7 @@ children = ( 84CE641E27981DED00142511 /* AppDelegate.swift */, 84CE642027981DED00142511 /* SceneDelegate.swift */, + 84CE645427A29D4C00142511 /* ResponseViewController.swift */, 84CE6449279EA1E600142511 /* AccountRequest */, 84CE6445279ABBF300142511 /* ClientDelegate.swift */, 84CE644C279ED2EC00142511 /* SelectChain */, @@ -451,6 +454,7 @@ buildActionMask = 2147483647; files = ( 84CE6430279820F600142511 /* AccountsViewController.swift in Sources */, + 84CE645527A29D4D00142511 /* ResponseViewController.swift in Sources */, 84CE6446279ABBF300142511 /* ClientDelegate.swift in Sources */, 84CE641F27981DED00142511 /* AppDelegate.swift in Sources */, 84CE6452279ED42B00142511 /* ConnectView.swift in Sources */, diff --git a/Example/ExampleApp/Responder/ResponderViewController.swift b/Example/ExampleApp/Responder/ResponderViewController.swift index 855520c35..f6025d63d 100644 --- a/Example/ExampleApp/Responder/ResponderViewController.swift +++ b/Example/ExampleApp/Responder/ResponderViewController.swift @@ -206,8 +206,15 @@ extension ResponderViewController: WalletConnectClientDelegate { } + func didReceive(sessionResponse: Response) { + + } + func didDelete(sessionTopic: String, reason: Reason) { reloadActiveSessions() + DispatchQueue.main.async { [unowned self] in + navigationController?.popToRootViewController(animated: true) + } } private func getActiveSessionItem(for settledSessions: [Session]) -> [ActiveSessionItem] { From 4ffaff05e205ca951cae62eff6752f18ca665bcb Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 27 Jan 2022 11:15:40 +0100 Subject: [PATCH 11/18] Update request view controller --- .../Request/RequestViewController.swift | 32 ++----------------- Example/ExampleApp/Responder/Signer.swift | 8 ++--- 2 files changed, 5 insertions(+), 35 deletions(-) diff --git a/Example/ExampleApp/Responder/Request/RequestViewController.swift b/Example/ExampleApp/Responder/Request/RequestViewController.swift index 0bac90b73..a6e392661 100644 --- a/Example/ExampleApp/Responder/Request/RequestViewController.swift +++ b/Example/ExampleApp/Responder/Request/RequestViewController.swift @@ -8,9 +8,8 @@ class RequestViewController: UIViewController { var onSign: (()->())? var onReject: (()->())? let sessionRequest: Request - private let requestView = { - RequestView() - }() + private let requestView = RequestView() + init(_ sessionRequest: Request) { self.sessionRequest = sessionRequest super.init(nibName: nil, bundle: nil) @@ -59,15 +58,6 @@ class RequestViewController: UIViewController { } final class RequestView: UIView { - - let iconView: UIImageView = { - let imageView = UIImageView() - imageView.contentMode = .scaleAspectFit - imageView.backgroundColor = .systemFill - imageView.layer.cornerRadius = 32 - return imageView - }() - let nameLabel: UILabel = { let label = UILabel() label.font = UIFont.systemFont(ofSize: 17.0, weight: .heavy) @@ -115,7 +105,6 @@ final class RequestView: UIView { super.init(frame: frame) backgroundColor = .systemBackground - addSubview(iconView) addSubview(headerStackView) addSubview(approveButton) addSubview(rejectButton) @@ -125,12 +114,8 @@ final class RequestView: UIView { subviews.forEach { $0.translatesAutoresizingMaskIntoConstraints = false } NSLayoutConstraint.activate([ - iconView.topAnchor.constraint(equalTo: topAnchor, constant: 64), - iconView.centerXAnchor.constraint(equalTo: centerXAnchor), - iconView.widthAnchor.constraint(equalToConstant: 64), - iconView.heightAnchor.constraint(equalToConstant: 64), - headerStackView.topAnchor.constraint(equalTo: iconView.bottomAnchor, constant: 32), + headerStackView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 32), headerStackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 32), headerStackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -32), @@ -148,17 +133,6 @@ final class RequestView: UIView { ]) } - func loadImage(at url: String) { - guard let iconURL = URL(string: url) else { return } - DispatchQueue.global().async { - if let imageData = try? Data(contentsOf: iconURL) { - DispatchQueue.main.async { [weak self] in - self?.iconView.image = UIImage(data: imageData) - } - } - } - } - required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } diff --git a/Example/ExampleApp/Responder/Signer.swift b/Example/ExampleApp/Responder/Signer.swift index 89482ac59..683bde6d3 100644 --- a/Example/ExampleApp/Responder/Signer.swift +++ b/Example/ExampleApp/Responder/Signer.swift @@ -16,12 +16,8 @@ class Signer { let result = "0x" + r.toHexString() + s.toHexString() + String(v + 27, radix: 16) return AnyCodable(result) } else if method == "eth_signTypedData" { - let params = try! request.params.get([String].self) - print(params) - let messageToSign = params[1] - let dataToHash = dataToHash(messageToSign) - let (v, r, s) = try! self.privateKey.sign(message: .init(hex: dataToHash.toHexString())) - let result = "0x" + r.toHexString() + s.toHexString() + String(v + 27, radix: 16) + //TODO + let result = "0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b915621c" return AnyCodable(result) } else if method == "eth_sendTransaction" { let params = try! request.params.get([EthereumTransaction].self) From 6414a4441ef588af33af532888e6b420c020ba43 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 27 Jan 2022 11:51:09 +0100 Subject: [PATCH 12/18] Filter pending requests by topic --- .../SessionDetails/SessionDetailsViewController.swift | 6 +++--- Sources/WalletConnect/WalletConnectClient.swift | 10 ++++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift b/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift index 688cc3c87..69fe928ad 100644 --- a/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift +++ b/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift @@ -10,7 +10,7 @@ final class SessionDetailsViewController: UIViewController, UITableViewDelegate, private let client: WalletConnectClient private let session: Session init(_ session: Session, _ client: WalletConnectClient) { - let pendingRequests = client.getPendingRequests().map{$0.method} + let pendingRequests = client.getPendingRequests(topic: session.topic).map{$0.method} self.sessionInfo = SessionInfo(name: session.peer.name ?? "", descriptionText: session.peer.description ?? "", dappURL: session.peer.description ?? "", @@ -99,7 +99,7 @@ final class SessionDetailsViewController: UIViewController, UITableViewDelegate, func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if indexPath.section == 2 { - let pendingRequests = client.getPendingRequests() + let pendingRequests = client.getPendingRequests(topic: session.topic) showSessionRequest(pendingRequests[indexPath.row]) } } @@ -120,7 +120,7 @@ final class SessionDetailsViewController: UIViewController, UITableViewDelegate, } func reloadTable() { - let pendingRequests = client.getPendingRequests().map{$0.method} + let pendingRequests = client.getPendingRequests(topic: session.topic).map{$0.method} self.sessionInfo = SessionInfo(name: session.peer.name ?? "", descriptionText: session.peer.description ?? "", dappURL: session.peer.description ?? "", diff --git a/Sources/WalletConnect/WalletConnectClient.swift b/Sources/WalletConnect/WalletConnectClient.swift index e7ffb41e3..3c3f38003 100644 --- a/Sources/WalletConnect/WalletConnectClient.swift +++ b/Sources/WalletConnect/WalletConnectClient.swift @@ -248,13 +248,19 @@ public final class WalletConnectClient { } /// - Returns: Pending requests received with wc_sessionPayload - public func getPendingRequests() -> [Request] { - history.getPending() + /// - Parameter topic: topic representing session for which you want to get pending requests. If nil, you will receive pending requests for all active sessions. + public func getPendingRequests(topic: String? = nil) -> [Request] { + let pendingRequests: [Request] = history.getPending() .filter{$0.request.method == .sessionPayload} .compactMap { guard case let .sessionPayload(payloadRequest) = $0.request.params else {return nil} return Request(id: $0.id, topic: $0.topic, method: payloadRequest.request.method, params: payloadRequest.request.params, chainId: payloadRequest.chainId) } + if let topic = topic { + return pendingRequests.filter{$0.topic == topic} + } else { + return pendingRequests + } } // MARK: - Private From 8e3fbb79d635a16bb98dd0270905647a0ed2e4cd Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 27 Jan 2022 13:27:09 +0100 Subject: [PATCH 13/18] change wallet config --- Example/ExampleApp.xcodeproj/project.pbxproj | 18 +++++++++--------- Example/ExampleApp/Info.plist | 2 +- .../WalletConnect/WalletConnectClient.swift | 7 +++++++ 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 58d30f1a7..59cd47f0e 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -63,7 +63,7 @@ 761C649D26FB7FD7004239D1 /* SessionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionViewController.swift; sourceTree = ""; }; 761C64A226FB83CE004239D1 /* SessionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionView.swift; sourceTree = ""; }; 761C64A526FCB0AA004239D1 /* SessionInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionInfo.swift; sourceTree = ""; }; - 764E1D3C26F8D3FC00A1FB15 /* Wallet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Wallet.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 764E1D3C26F8D3FC00A1FB15 /* WalletConnect Wallet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "WalletConnect Wallet.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 764E1D3F26F8D3FC00A1FB15 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 764E1D4126F8D3FC00A1FB15 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 764E1D4826F8D3FE00A1FB15 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -171,7 +171,7 @@ 764E1D3D26F8D3FC00A1FB15 /* Products */ = { isa = PBXGroup; children = ( - 764E1D3C26F8D3FC00A1FB15 /* Wallet.app */, + 764E1D3C26F8D3FC00A1FB15 /* WalletConnect Wallet.app */, 845B30EC27859686002E4094 /* ExampleAppTests.xctest */, 84CE641C27981DED00142511 /* DApp.app */, ); @@ -303,7 +303,7 @@ 844943A0278EC49700CC26BB /* Web3 */, ); productName = ExampleApp; - productReference = 764E1D3C26F8D3FC00A1FB15 /* Wallet.app */; + productReference = 764E1D3C26F8D3FC00A1FB15 /* WalletConnect Wallet.app */; productType = "com.apple.product-type.application"; }; 845B30EB27859686002E4094 /* ExampleAppTests */ = { @@ -622,7 +622,7 @@ CODE_SIGN_ENTITLEMENTS = Wallet.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 7; + CURRENT_PROJECT_VERSION = 9; DEVELOPMENT_TEAM = W5R8AG9K22; INFOPLIST_FILE = ExampleApp/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; @@ -632,7 +632,7 @@ ); MARKETING_VERSION = 0.0.1; PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.example; - PRODUCT_NAME = "$(TARGET_NAME)"; + PRODUCT_NAME = "WalletConnect Wallet"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -647,7 +647,7 @@ CODE_SIGN_ENTITLEMENTS = Wallet.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 7; + CURRENT_PROJECT_VERSION = 9; DEVELOPMENT_TEAM = W5R8AG9K22; INFOPLIST_FILE = ExampleApp/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; @@ -657,7 +657,7 @@ ); MARKETING_VERSION = 0.0.1; PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.example; - PRODUCT_NAME = "$(TARGET_NAME)"; + PRODUCT_NAME = "WalletConnect Wallet"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -711,7 +711,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = W5R8AG9K22; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = DApp/Info.plist; @@ -743,7 +743,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = W5R8AG9K22; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = DApp/Info.plist; diff --git a/Example/ExampleApp/Info.plist b/Example/ExampleApp/Info.plist index 8f53cd324..fd00d23da 100644 --- a/Example/ExampleApp/Info.plist +++ b/Example/ExampleApp/Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - Demo App + WalletConnect Wallet CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier diff --git a/Sources/WalletConnect/WalletConnectClient.swift b/Sources/WalletConnect/WalletConnectClient.swift index 3c3f38003..c10609373 100644 --- a/Sources/WalletConnect/WalletConnectClient.swift +++ b/Sources/WalletConnect/WalletConnectClient.swift @@ -263,6 +263,13 @@ public final class WalletConnectClient { } } +// /// <#Description#> +// /// - Parameter id: <#id description#> +// /// - Returns: <#description#> +// public func getSessionRequestRecord(id: Int64) -> [WalletConnectUtils.JsonRpcRecord] { +// +// } + // MARK: - Private private func setUpEnginesCallbacks() { From 593e5e43c6d2f0942ea0ea3f3474f05c885214ea Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 27 Jan 2022 13:42:28 +0100 Subject: [PATCH 14/18] Add Dapp test target, update test configuration, update fastfile --- Example/DappTests/DappTests.swift | 35 +++++ Example/ExampleApp.xcodeproj/project.pbxproj | 136 +++++++++++++++++- .../xcshareddata/xcschemes/DApp.xcscheme | 10 ++ .../xcshareddata/xcschemes/Wallet.xcscheme | 10 ++ Example/fastlane/Fastfile | 9 +- 5 files changed, 196 insertions(+), 4 deletions(-) create mode 100644 Example/DappTests/DappTests.swift diff --git a/Example/DappTests/DappTests.swift b/Example/DappTests/DappTests.swift new file mode 100644 index 000000000..2792b20c0 --- /dev/null +++ b/Example/DappTests/DappTests.swift @@ -0,0 +1,35 @@ +// +// DappTests.swift +// DappTests +// +// Created by Admin on 27/01/2022. +// + +import XCTest + +class DappTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + // Any test you write for XCTest can be annotated as throws and async. + // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. + // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 59cd47f0e..185c204aa 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -42,6 +42,7 @@ 84CE644E279ED2FF00142511 /* SelectChainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE644D279ED2FF00142511 /* SelectChainView.swift */; }; 84CE6452279ED42B00142511 /* ConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE6451279ED42B00142511 /* ConnectView.swift */; }; 84CE645527A29D4D00142511 /* ResponseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE645427A29D4C00142511 /* ResponseViewController.swift */; }; + 84CE646127A2C85B00142511 /* DappTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE646027A2C85B00142511 /* DappTests.swift */; }; 84F568C2279582D200D0A289 /* Signer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C1279582D200D0A289 /* Signer.swift */; }; 84F568C42795832A00D0A289 /* EthereumTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C32795832A00D0A289 /* EthereumTransaction.swift */; }; /* End PBXBuildFile section */ @@ -54,6 +55,20 @@ remoteGlobalIDString = 764E1D3B26F8D3FC00A1FB15; remoteInfo = ExampleApp; }; + 84CE646227A2C85B00142511 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 764E1D3426F8D3FC00A1FB15 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 764E1D3B26F8D3FC00A1FB15; + remoteInfo = Wallet; + }; + 84CE646727A2C86100142511 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 764E1D3426F8D3FC00A1FB15 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 84CE641B27981DED00142511; + remoteInfo = DApp; + }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ @@ -97,6 +112,8 @@ 84CE6451279ED42B00142511 /* ConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectView.swift; sourceTree = ""; }; 84CE6453279FFE1100142511 /* Wallet.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Wallet.entitlements; sourceTree = ""; }; 84CE645427A29D4C00142511 /* ResponseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResponseViewController.swift; sourceTree = ""; }; + 84CE645E27A2C85B00142511 /* DappTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DappTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 84CE646027A2C85B00142511 /* DappTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DappTests.swift; sourceTree = ""; }; 84F568C1279582D200D0A289 /* Signer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Signer.swift; sourceTree = ""; }; 84F568C32795832A00D0A289 /* EthereumTransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EthereumTransaction.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -127,6 +144,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 84CE645B27A2C85B00142511 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -162,6 +186,7 @@ 764E1D3E26F8D3FC00A1FB15 /* ExampleApp */, 845B30ED27859686002E4094 /* ExampleAppTests */, 84CE641D27981DED00142511 /* DApp */, + 84CE645F27A2C85B00142511 /* DappTests */, 764E1D3D26F8D3FC00A1FB15 /* Products */, 764E1D5326F8DAC800A1FB15 /* Frameworks */, 764E1D5626F8DB6000A1FB15 /* WalletConnectSwiftV2 */, @@ -174,6 +199,7 @@ 764E1D3C26F8D3FC00A1FB15 /* WalletConnect Wallet.app */, 845B30EC27859686002E4094 /* ExampleAppTests.xctest */, 84CE641C27981DED00142511 /* DApp.app */, + 84CE645E27A2C85B00142511 /* DappTests.xctest */, ); name = Products; sourceTree = ""; @@ -282,6 +308,14 @@ path = Connect; sourceTree = ""; }; + 84CE645F27A2C85B00142511 /* DappTests */ = { + isa = PBXGroup; + children = ( + 84CE646027A2C85B00142511 /* DappTests.swift */, + ); + path = DappTests; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -345,6 +379,25 @@ productReference = 84CE641C27981DED00142511 /* DApp.app */; productType = "com.apple.product-type.application"; }; + 84CE645D27A2C85B00142511 /* DappTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 84CE646427A2C85B00142511 /* Build configuration list for PBXNativeTarget "DappTests" */; + buildPhases = ( + 84CE645A27A2C85B00142511 /* Sources */, + 84CE645B27A2C85B00142511 /* Frameworks */, + 84CE645C27A2C85B00142511 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 84CE646327A2C85B00142511 /* PBXTargetDependency */, + 84CE646827A2C86100142511 /* PBXTargetDependency */, + ); + name = DappTests; + productName = DappTests; + productReference = 84CE645E27A2C85B00142511 /* DappTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -364,6 +417,10 @@ 84CE641B27981DED00142511 = { CreatedOnToolsVersion = 13.2; }; + 84CE645D27A2C85B00142511 = { + CreatedOnToolsVersion = 13.2; + TestTargetID = 84CE641B27981DED00142511; + }; }; }; buildConfigurationList = 764E1D3726F8D3FC00A1FB15 /* Build configuration list for PBXProject "ExampleApp" */; @@ -385,6 +442,7 @@ 764E1D3B26F8D3FC00A1FB15 /* Wallet */, 845B30EB27859686002E4094 /* ExampleAppTests */, 84CE641B27981DED00142511 /* DApp */, + 84CE645D27A2C85B00142511 /* DappTests */, ); }; /* End PBXProject section */ @@ -415,6 +473,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 84CE645C27A2C85B00142511 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -468,6 +533,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 84CE645A27A2C85B00142511 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 84CE646127A2C85B00142511 /* DappTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -476,6 +549,16 @@ target = 764E1D3B26F8D3FC00A1FB15 /* Wallet */; targetProxy = 845B30F027859686002E4094 /* PBXContainerItemProxy */; }; + 84CE646327A2C85B00142511 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 764E1D3B26F8D3FC00A1FB15 /* Wallet */; + targetProxy = 84CE646227A2C85B00142511 /* PBXContainerItemProxy */; + }; + 84CE646827A2C86100142511 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 84CE641B27981DED00142511 /* DApp */; + targetProxy = 84CE646727A2C86100142511 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -680,7 +763,7 @@ SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Wallet.app/Wallet"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WalletConnect Wallet.app/WalletConnect Wallet"; }; name = Debug; }; @@ -700,7 +783,7 @@ SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Wallet.app/Wallet"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WalletConnect Wallet.app/WalletConnect Wallet"; }; name = Release; }; @@ -768,6 +851,46 @@ }; name = Release; }; + 84CE646527A2C85B00142511 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = W5R8AG9K22; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = walletconnect.DappTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DApp.app/DApp"; + }; + name = Debug; + }; + 84CE646627A2C85B00142511 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = W5R8AG9K22; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = walletconnect.DappTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DApp.app/DApp"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -807,6 +930,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 84CE646427A2C85B00142511 /* Build configuration list for PBXNativeTarget "DappTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 84CE646527A2C85B00142511 /* Debug */, + 84CE646627A2C85B00142511 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ diff --git a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/DApp.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/DApp.xcscheme index 60a72b1ae..bb02c9037 100644 --- a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/DApp.xcscheme +++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/DApp.xcscheme @@ -28,6 +28,16 @@ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> + + + + + + + + Date: Thu, 27 Jan 2022 13:44:42 +0100 Subject: [PATCH 15/18] update swift.yml --- .github/workflows/swift.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index d47aa4129..ca91b0389 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -19,7 +19,11 @@ jobs: run: swift build -v - name: Run tests run: swift test -v - - name: Test Example App + - name: Test Wallet working-directory: ./Example - run: fastlane test_app + run: fastlane test_wallet + - name: Test Dapp + working-directory: ./Example + run: fastlane test_dapp + From 104a6c04b1ea4dddf9e87aea05c104dc767cf199 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 27 Jan 2022 13:55:37 +0100 Subject: [PATCH 16/18] rename test target for wallet --- Example/ExampleApp.xcodeproj/project.pbxproj | 230 +++++++++--------- .../xcshareddata/xcschemes/Wallet.xcscheme | 6 +- Example/ExampleAppTests/ExampleAppTests.swift | 5 - Example/WalletTests/WalletTests.swift | 35 +++ 4 files changed, 153 insertions(+), 123 deletions(-) delete mode 100644 Example/ExampleAppTests/ExampleAppTests.swift create mode 100644 Example/WalletTests/WalletTests.swift diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 185c204aa..5bd25f858 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -22,7 +22,6 @@ 76744CF926FE4D7400B77ED9 /* ActiveSessionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76744CF826FE4D7400B77ED9 /* ActiveSessionCell.swift */; }; 84494388278D9C1B00CC26BB /* UIAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84494387278D9C1B00CC26BB /* UIAlertController.swift */; }; 844943A1278EC49700CC26BB /* Web3 in Frameworks */ = {isa = PBXBuildFile; productRef = 844943A0278EC49700CC26BB /* Web3 */; }; - 845B30EF27859686002E4094 /* ExampleAppTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845B30EE27859686002E4094 /* ExampleAppTests.swift */; }; 8460DCFC274F98A10081F94C /* RequestViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8460DCFB274F98A10081F94C /* RequestViewController.swift */; }; 8460DD002750D6F50081F94C /* SessionDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8460DCFF2750D6F50081F94C /* SessionDetailsViewController.swift */; }; 8460DD022750D7020081F94C /* SessionDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8460DD012750D7020081F94C /* SessionDetailsView.swift */; }; @@ -43,18 +42,12 @@ 84CE6452279ED42B00142511 /* ConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE6451279ED42B00142511 /* ConnectView.swift */; }; 84CE645527A29D4D00142511 /* ResponseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE645427A29D4C00142511 /* ResponseViewController.swift */; }; 84CE646127A2C85B00142511 /* DappTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE646027A2C85B00142511 /* DappTests.swift */; }; + 84CE647027A2CD6B00142511 /* WalletTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE646F27A2CD6B00142511 /* WalletTests.swift */; }; 84F568C2279582D200D0A289 /* Signer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C1279582D200D0A289 /* Signer.swift */; }; 84F568C42795832A00D0A289 /* EthereumTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C32795832A00D0A289 /* EthereumTransaction.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 845B30F027859686002E4094 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 764E1D3426F8D3FC00A1FB15 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 764E1D3B26F8D3FC00A1FB15; - remoteInfo = ExampleApp; - }; 84CE646227A2C85B00142511 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 764E1D3426F8D3FC00A1FB15 /* Project object */; @@ -69,6 +62,13 @@ remoteGlobalIDString = 84CE641B27981DED00142511; remoteInfo = DApp; }; + 84CE647127A2CD6B00142511 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 764E1D3426F8D3FC00A1FB15 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 764E1D3B26F8D3FC00A1FB15; + remoteInfo = Wallet; + }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ @@ -92,8 +92,6 @@ 76744CF626FE4D5400B77ED9 /* ActiveSessionItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveSessionItem.swift; sourceTree = ""; }; 76744CF826FE4D7400B77ED9 /* ActiveSessionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveSessionCell.swift; sourceTree = ""; }; 84494387278D9C1B00CC26BB /* UIAlertController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIAlertController.swift; sourceTree = ""; }; - 845B30EC27859686002E4094 /* ExampleAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ExampleAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 845B30EE27859686002E4094 /* ExampleAppTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleAppTests.swift; sourceTree = ""; }; 8460DCFB274F98A10081F94C /* RequestViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestViewController.swift; sourceTree = ""; }; 8460DCFF2750D6F50081F94C /* SessionDetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionDetailsViewController.swift; sourceTree = ""; }; 8460DD012750D7020081F94C /* SessionDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionDetailsView.swift; sourceTree = ""; }; @@ -114,6 +112,8 @@ 84CE645427A29D4C00142511 /* ResponseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResponseViewController.swift; sourceTree = ""; }; 84CE645E27A2C85B00142511 /* DappTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DappTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 84CE646027A2C85B00142511 /* DappTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DappTests.swift; sourceTree = ""; }; + 84CE646D27A2CD6B00142511 /* WalletTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WalletTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 84CE646F27A2CD6B00142511 /* WalletTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletTests.swift; sourceTree = ""; }; 84F568C1279582D200D0A289 /* Signer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Signer.swift; sourceTree = ""; }; 84F568C32795832A00D0A289 /* EthereumTransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EthereumTransaction.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -128,23 +128,23 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 845B30E927859686002E4094 /* Frameworks */ = { + 84CE641927981DED00142511 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 84CE643B2798229100142511 /* WalletConnect in Frameworks */, + 84CE64392798228D00142511 /* Web3 in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - 84CE641927981DED00142511 /* Frameworks */ = { + 84CE645B27A2C85B00142511 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 84CE643B2798229100142511 /* WalletConnect in Frameworks */, - 84CE64392798228D00142511 /* Web3 in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - 84CE645B27A2C85B00142511 /* Frameworks */ = { + 84CE646A27A2CD6B00142511 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( @@ -184,9 +184,9 @@ children = ( 84CE6453279FFE1100142511 /* Wallet.entitlements */, 764E1D3E26F8D3FC00A1FB15 /* ExampleApp */, - 845B30ED27859686002E4094 /* ExampleAppTests */, 84CE641D27981DED00142511 /* DApp */, 84CE645F27A2C85B00142511 /* DappTests */, + 84CE646E27A2CD6B00142511 /* WalletTests */, 764E1D3D26F8D3FC00A1FB15 /* Products */, 764E1D5326F8DAC800A1FB15 /* Frameworks */, 764E1D5626F8DB6000A1FB15 /* WalletConnectSwiftV2 */, @@ -197,9 +197,9 @@ isa = PBXGroup; children = ( 764E1D3C26F8D3FC00A1FB15 /* WalletConnect Wallet.app */, - 845B30EC27859686002E4094 /* ExampleAppTests.xctest */, 84CE641C27981DED00142511 /* DApp.app */, 84CE645E27A2C85B00142511 /* DappTests.xctest */, + 84CE646D27A2CD6B00142511 /* WalletTests.xctest */, ); name = Products; sourceTree = ""; @@ -229,14 +229,6 @@ name = Frameworks; sourceTree = ""; }; - 845B30ED27859686002E4094 /* ExampleAppTests */ = { - isa = PBXGroup; - children = ( - 845B30EE27859686002E4094 /* ExampleAppTests.swift */, - ); - path = ExampleAppTests; - sourceTree = ""; - }; 8460DCFD274F98A90081F94C /* Request */ = { isa = PBXGroup; children = ( @@ -316,6 +308,14 @@ path = DappTests; sourceTree = ""; }; + 84CE646E27A2CD6B00142511 /* WalletTests */ = { + isa = PBXGroup; + children = ( + 84CE646F27A2CD6B00142511 /* WalletTests.swift */, + ); + path = WalletTests; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -340,24 +340,6 @@ productReference = 764E1D3C26F8D3FC00A1FB15 /* WalletConnect Wallet.app */; productType = "com.apple.product-type.application"; }; - 845B30EB27859686002E4094 /* ExampleAppTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 845B30F227859686002E4094 /* Build configuration list for PBXNativeTarget "ExampleAppTests" */; - buildPhases = ( - 845B30E827859686002E4094 /* Sources */, - 845B30E927859686002E4094 /* Frameworks */, - 845B30EA27859686002E4094 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 845B30F127859686002E4094 /* PBXTargetDependency */, - ); - name = ExampleAppTests; - productName = ExampleAppTests; - productReference = 845B30EC27859686002E4094 /* ExampleAppTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; 84CE641B27981DED00142511 /* DApp */ = { isa = PBXNativeTarget; buildConfigurationList = 84CE642F27981DF000142511 /* Build configuration list for PBXNativeTarget "DApp" */; @@ -398,6 +380,24 @@ productReference = 84CE645E27A2C85B00142511 /* DappTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + 84CE646C27A2CD6B00142511 /* WalletTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 84CE647327A2CD6C00142511 /* Build configuration list for PBXNativeTarget "WalletTests" */; + buildPhases = ( + 84CE646927A2CD6B00142511 /* Sources */, + 84CE646A27A2CD6B00142511 /* Frameworks */, + 84CE646B27A2CD6B00142511 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 84CE647227A2CD6B00142511 /* PBXTargetDependency */, + ); + name = WalletTests; + productName = WalletTests; + productReference = 84CE646D27A2CD6B00142511 /* WalletTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -410,10 +410,6 @@ 764E1D3B26F8D3FC00A1FB15 = { CreatedOnToolsVersion = 12.5.1; }; - 845B30EB27859686002E4094 = { - CreatedOnToolsVersion = 13.2; - TestTargetID = 764E1D3B26F8D3FC00A1FB15; - }; 84CE641B27981DED00142511 = { CreatedOnToolsVersion = 13.2; }; @@ -421,6 +417,10 @@ CreatedOnToolsVersion = 13.2; TestTargetID = 84CE641B27981DED00142511; }; + 84CE646C27A2CD6B00142511 = { + CreatedOnToolsVersion = 13.2; + TestTargetID = 764E1D3B26F8D3FC00A1FB15; + }; }; }; buildConfigurationList = 764E1D3726F8D3FC00A1FB15 /* Build configuration list for PBXProject "ExampleApp" */; @@ -440,9 +440,9 @@ projectRoot = ""; targets = ( 764E1D3B26F8D3FC00A1FB15 /* Wallet */, - 845B30EB27859686002E4094 /* ExampleAppTests */, 84CE641B27981DED00142511 /* DApp */, 84CE645D27A2C85B00142511 /* DappTests */, + 84CE646C27A2CD6B00142511 /* WalletTests */, ); }; /* End PBXProject section */ @@ -457,23 +457,23 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 845B30EA27859686002E4094 /* Resources */ = { + 84CE641A27981DED00142511 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 84CE642B27981DF000142511 /* LaunchScreen.storyboard in Resources */, + 84CE642827981DF000142511 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 84CE641A27981DED00142511 /* Resources */ = { + 84CE645C27A2C85B00142511 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 84CE642B27981DF000142511 /* LaunchScreen.storyboard in Resources */, - 84CE642827981DF000142511 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 84CE645C27A2C85B00142511 /* Resources */ = { + 84CE646B27A2CD6B00142511 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( @@ -506,14 +506,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 845B30E827859686002E4094 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 845B30EF27859686002E4094 /* ExampleAppTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 84CE641827981DED00142511 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -541,14 +533,17 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 84CE646927A2CD6B00142511 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 84CE647027A2CD6B00142511 /* WalletTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - 845B30F127859686002E4094 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 764E1D3B26F8D3FC00A1FB15 /* Wallet */; - targetProxy = 845B30F027859686002E4094 /* PBXContainerItemProxy */; - }; 84CE646327A2C85B00142511 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 764E1D3B26F8D3FC00A1FB15 /* Wallet */; @@ -559,6 +554,11 @@ target = 84CE641B27981DED00142511 /* DApp */; targetProxy = 84CE646727A2C86100142511 /* PBXContainerItemProxy */; }; + 84CE647227A2CD6B00142511 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 764E1D3B26F8D3FC00A1FB15 /* Wallet */; + targetProxy = 84CE647127A2CD6B00142511 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -747,46 +747,6 @@ }; name = Release; }; - 845B30F327859686002E4094 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = W5R8AG9K22; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = walletconnect.ExampleAppTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WalletConnect Wallet.app/WalletConnect Wallet"; - }; - name = Debug; - }; - 845B30F427859686002E4094 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = W5R8AG9K22; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = walletconnect.ExampleAppTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WalletConnect Wallet.app/WalletConnect Wallet"; - }; - name = Release; - }; 84CE642D27981DF000142511 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -891,6 +851,46 @@ }; name = Release; }; + 84CE647427A2CD6C00142511 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = W5R8AG9K22; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = walletconnect.WalletTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WalletConnect Wallet.app/WalletConnect Wallet"; + }; + name = Debug; + }; + 84CE647527A2CD6C00142511 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = W5R8AG9K22; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = walletconnect.WalletTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WalletConnect Wallet.app/WalletConnect Wallet"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -912,15 +912,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 845B30F227859686002E4094 /* Build configuration list for PBXNativeTarget "ExampleAppTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 845B30F327859686002E4094 /* Debug */, - 845B30F427859686002E4094 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 84CE642F27981DF000142511 /* Build configuration list for PBXNativeTarget "DApp" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -939,6 +930,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 84CE647327A2CD6C00142511 /* Build configuration list for PBXNativeTarget "WalletTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 84CE647427A2CD6C00142511 /* Debug */, + 84CE647527A2CD6C00142511 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ diff --git a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/Wallet.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/Wallet.xcscheme index 36510de9a..16dc75af4 100644 --- a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/Wallet.xcscheme +++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/Wallet.xcscheme @@ -32,9 +32,9 @@ skipped = "NO"> diff --git a/Example/ExampleAppTests/ExampleAppTests.swift b/Example/ExampleAppTests/ExampleAppTests.swift deleted file mode 100644 index e16431ce7..000000000 --- a/Example/ExampleAppTests/ExampleAppTests.swift +++ /dev/null @@ -1,5 +0,0 @@ - -import XCTest - -class ExampleAppTests: XCTestCase { -} diff --git a/Example/WalletTests/WalletTests.swift b/Example/WalletTests/WalletTests.swift new file mode 100644 index 000000000..a248726c3 --- /dev/null +++ b/Example/WalletTests/WalletTests.swift @@ -0,0 +1,35 @@ +// +// WalletTests.swift +// WalletTests +// +// Created by Admin on 27/01/2022. +// + +import XCTest + +class WalletTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + // Any test you write for XCTest can be annotated as throws and async. + // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. + // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + measure { + // Put the code you want to measure the time of here. + } + } + +} From aecc9db5c7a709803d4afcfd00b4d88345b5b482 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 27 Jan 2022 14:45:32 +0100 Subject: [PATCH 17/18] fix scheme typo in fastfile --- Example/fastlane/Fastfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Example/fastlane/Fastfile b/Example/fastlane/Fastfile index 3bac2c332..45180026e 100644 --- a/Example/fastlane/Fastfile +++ b/Example/fastlane/Fastfile @@ -9,6 +9,6 @@ platform :ios do desc "Test Example dapp" lane :test_dapp do - scan(xcargs: "-allowProvisioningUpdates", scheme: "Dapp") + scan(xcargs: "-allowProvisioningUpdates", scheme: "DApp") end end From f694e298ba82e92d28baf05e0ee2a1b9fb03cb07 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 28 Jan 2022 13:49:35 +0100 Subject: [PATCH 18/18] add query session request record by jsonrpc id --- Example/DApp/ResponseViewController.swift | 6 ++++-- Sources/WalletConnect/WalletConnectClient.swift | 14 ++++++++------ Sources/WalletConnectUtils/JsonRpcRecord.swift | 13 +++++++++++++ 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/Example/DApp/ResponseViewController.swift b/Example/DApp/ResponseViewController.swift index e53f89688..e85baaafe 100644 --- a/Example/DApp/ResponseViewController.swift +++ b/Example/DApp/ResponseViewController.swift @@ -24,12 +24,13 @@ class ResponseViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() + let record = ClientDelegate.shared.client.getSessionRequestRecord(id: response.result.id)! switch response.result { case .response(let response): - responseView.nameLabel.text = "Received Response" + responseView.nameLabel.text = "Received Response\n\(record.request.method)" responseView.descriptionLabel.text = try! response.result.get(String.self).description case .error(let error): - responseView.nameLabel.text = "Received Error" + responseView.nameLabel.text = "Received Error\n\(record.request.method)" responseView.descriptionLabel.text = error.error.message } responseView.dismissButton.addTarget(self, action: #selector(dismissSelf), for: .touchUpInside) @@ -45,6 +46,7 @@ final class ResponseView: UIView { let nameLabel: UILabel = { let label = UILabel() + label.numberOfLines = 0 label.font = UIFont.systemFont(ofSize: 17.0, weight: .heavy) return label }() diff --git a/Sources/WalletConnect/WalletConnectClient.swift b/Sources/WalletConnect/WalletConnectClient.swift index c10609373..c79d0097e 100644 --- a/Sources/WalletConnect/WalletConnectClient.swift +++ b/Sources/WalletConnect/WalletConnectClient.swift @@ -263,12 +263,14 @@ public final class WalletConnectClient { } } -// /// <#Description#> -// /// - Parameter id: <#id description#> -// /// - Returns: <#description#> -// public func getSessionRequestRecord(id: Int64) -> [WalletConnectUtils.JsonRpcRecord] { -// -// } + /// - Parameter id: id of a wc_sessionPayload jsonrpc request + /// - Returns: json rpc record object for given id or nil if record for give id does not exits + public func getSessionRequestRecord(id: Int64) -> WalletConnectUtils.JsonRpcRecord? { + guard let record = history.get(id: id), + case .sessionPayload(let payload) = record.request.params else {return nil} + let request = WalletConnectUtils.JsonRpcRecord.Request(method: payload.request.method, params: payload.request.params) + return WalletConnectUtils.JsonRpcRecord(id: record.id, topic: record.topic, request: request, response: record.response, chainId: record.chainId) + } // MARK: - Private diff --git a/Sources/WalletConnectUtils/JsonRpcRecord.swift b/Sources/WalletConnectUtils/JsonRpcRecord.swift index 598d17dfe..151f51651 100644 --- a/Sources/WalletConnectUtils/JsonRpcRecord.swift +++ b/Sources/WalletConnectUtils/JsonRpcRecord.swift @@ -8,9 +8,22 @@ public struct JsonRpcRecord: Codable { public var response: JsonRpcResult? public let chainId: String? + public init(id: Int64, topic: String, request: JsonRpcRecord.Request, response: JsonRpcResult? = nil, chainId: String?) { + self.id = id + self.topic = topic + self.request = request + self.response = response + self.chainId = chainId + } + public struct Request: Codable { public let method: String public let params: AnyCodable + + public init(method: String, params: AnyCodable) { + self.method = method + self.params = params + } } }