From f0edd0e6cbccbc352227b6ddc2404c3dd52fe6e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Oct 2024 18:41:35 +0000 Subject: [PATCH 01/12] build(deps): Bump rexml from 3.3.6 to 3.3.9 Bumps [rexml](https://github.com/ruby/rexml) from 3.3.6 to 3.3.9. - [Release notes](https://github.com/ruby/rexml/releases) - [Changelog](https://github.com/ruby/rexml/blob/master/NEWS.md) - [Commits](https://github.com/ruby/rexml/compare/v3.3.6...v3.3.9) --- updated-dependencies: - dependency-name: rexml dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 7af99e226..57adcd5ab 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -70,13 +70,11 @@ GEM public_suffix (5.0.1) rake (13.0.6) rchardet (1.8.0) - rexml (3.3.6) - strscan + rexml (3.3.9) ruby2_keywords (0.0.5) sawyer (0.9.2) addressable (>= 2.3.5) faraday (>= 0.17.3, < 3) - strscan (3.1.0) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) thor (0.20.3) From 587a60f27367cc95fc65d7a6e39a4c5aa6c4ef59 Mon Sep 17 00:00:00 2001 From: Jakub Kaspar Date: Wed, 30 Oct 2024 07:12:16 +0100 Subject: [PATCH 02/12] Fix example app --- .../bobbly.imageset/Group 1272628320.png | Bin 490 -> 1127 bytes .../bobbly.imageset/Group 1272628320@2x.png | Bin 953 -> 1894 bytes .../bobbly.imageset/Group 1272628320@3x.png | Bin 1446 -> 2798 bytes Sources/Models/MessageStyle.swift | 12 ++++++------ 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Example/Sources/Resources/Assets.xcassets/bobbly.imageset/Group 1272628320.png b/Example/Sources/Resources/Assets.xcassets/bobbly.imageset/Group 1272628320.png index faaad6d77c0392bd4026623a613162e1ea6ad17b..be5d8ca35c953a5264910a12e0002bfea6897d08 100644 GIT binary patch literal 1127 zcmeAS@N?(olHy`uVBq!ia0vp^Wk4*?!3HFaVl1116id3JuOkD)#(wTUiL5}rLb6AY zF9SoB8UsT^3j@P1pisjL28L1t28LG&3=CE?7#PG0=Ijcz0ZO!_MtG+A`Z8z%*&Gb4 zj6w{|Ko%nqGB8RpfJ_6j7`zyz;p`wr4X7F>28Q-b1{SEAC?E|29zYDz1EHB0Fd|G` zzyw!awtyMVmIrB@_%0jhUIxwrkH}&M25w;xW@MN(M*?VCWM)W2NrbPDRdRl=USdjq zQmS4>ZUIOagH44MkeQoWlBiITo0C^;Rbi_HR$&EXgM{^!6u?SKvTcQY`gxM_2QNxtuL-K zKfCVOuqfbfwZYbt^By%ErReQVk5yLQ2?wajamRx~Ak_#P_J z&V1%G`<#u3IAjwx%ogl>Ezurw%hCA8Qqhpzr3<#$rf)gHc-7q`}3o%~$-N`1MMqw5yF z4?Ej8Z|A>q{lTiN=e6RV-PapgVoy7;ow#}0jCrMTMAp{6AcSes)fn#A$UQ)r`_c9XH?}qnX*|4 z+8u>cHjy$6T$U#2?j;F`3gqSK8dV)Och}HTN7`2)YLGzdH4#}C+LIscxx44Is|*RV p4mJmg0y!}Lj_MBT_`m=F002ovPDHLkV1n~U$}|7~ diff --git a/Example/Sources/Resources/Assets.xcassets/bobbly.imageset/Group 1272628320@2x.png b/Example/Sources/Resources/Assets.xcassets/bobbly.imageset/Group 1272628320@2x.png index bdbde8f5a9a9162744c1574c2599e2ad002b92f3..11eb26a186daa50facb631c182970aea19d72f52 100644 GIT binary patch literal 1894 zcmY*a3s@6Z7QPuq4HViysvw#5>)g@#3>Yr)+>Sw&g`mA#`Z-JSXFoHOVC=Rg0s_s%yZ zQIc>MCl4n8fQxX0Knkr54jz6Xe0tM<@r4H69wH6_;Ns!8W|RxyJ0*UDR183#Cjh#= z0N7zjHv&Ko#QswO0J{Q!r5QC>H*w&^-b9%&Nh}7dp^pa(aBjc>dN_CkoCmfRE5StCa z5*jveLg{sg&0c%?>VACXS+nre`Hm0vWyxUnBnNRn7;l@e}6he(6e`?X_Q)eTK1|pB>&|RBxc8FsWLRG^fU^_tBgy} z(Qv6$tkC@RMyE!V^n0bW?76mJgG_9O>BsP8&U3?54i;rcW~mZkWsINa$9awXzt~(I z4ihW>KVsf=`Z@|*?!IP2X4iSjkBckD zq_?-QcPu&;Gf4ZuD7_}yenoWpvW8|lD`@8T>SKQ>&$`ga+Fv@pkW#YZEBgTJghTiB z%0-0_xej~SmYzms{Mo0gpT%@_K9i^5@iS4V$Bn8(rsE8Q-7fn^n;BrX1pc(+Y-y>0 zKlqtji$2nDSW4d=bJ5aQBZ|QK7+#s=hNcmEM=HhFZuVGTbVZVPG1rZeL=|lg?{*MIgyyZSr)|x(jiwGpTh&sG zJrmD)&>A#qI;t#N61SZ^ERNfZPe4fIUXyriB*p4n{e|t}y2?yEe^7xO`nW}<9_UV< zmUnLZ`^*5V48`*mcw~UGLR4>GyEA`(b3j+_UPl;|BazX@XRCKjGyYv|bUYXekVe8t z+om*9yG12KQHn2DZzw%yY_*>Fa;NsuI;euL2tal;+elH$e~{ay#UJtk^1@ZtWLg{+ zXv?hlD}XsP{r4y1rb>UkRJ!D(;11L-#0NAD1>I?WLjLX(-z4?lLcLH=uOrbtb*V0- z_7w7GM1)XrfWLTgyRts)(+Mcz5@ExpM+L&=haS9x!*7hF^b6E7-Yv;&ag-}ULwr0Y zwhWz1K2iw91R_(U8j{x_8QYmLN1SthiD(Az z60Dj(@@mE|ivA|(eB-OhyAH6|KkDB(NxWOUJ0>E|8Fq2d9DFjOujSPdQsK9By4f8f z-1YJ`vC7ohr?VICvEP1w8Ps{lb39@}mGo%A1E1lH7d~YT`^+XZr96LNv;Q`?;p^Rw ztfNa1g8MPEm3L0P%`K>C)ztCl>4odzT9*;V#gA{#f<#Hs1@2;FH5ca?H20+#!Ex#Of`u&S>jcqGan9X=y|J^VYJFe z<$26zL+H?_o)_MZ)myM?}9PMI4O88ZW5jOTt{I+E%8HXJprDMU` zwO=p}`k^@d*L=#^Bx~ZuX-*j2uE)#th8L`|*38Hp+s*J<-04%*kJpZBow2`%@I#5f IxL&d6KL;JpH2?qr delta 914 zcmV;D18w}~4!H-AB$4nGe*gh> zQ>1X{^h_q52L8XQ2Qz`8*nIUmola*L>Bepc2 z%GaT<6NP{RPgSE3+%s)3yqi+_I`?%$QPh9+TcL@qWs@DY@^#vN2N2izI%vNqd#2%R zxcgC|t5yP#!uh&V$WHPp`V1dXq~ zzRNma1fT@9e|_l->~{eufjzywH?Xxhmj$MF(N@w*r}qLdrL)D0)Sdr%x;ik8#@F8B zP3l@=x-2k_gYKKuhha6oc744PfLv@S{Iz=^wZl5GWekv0>ibs#qj>(aISeM4e8E0EV4MQ*&=zW;Oof1c&x1Rw_+2HT8q^e(wrhdmU4 z92!N2!CigN)&VdsyL7X3f@vA{B?hKZD=bK=SNYJk0OV9_ji4^X0^4Z#q|>}DC*bL_ z4A{NpcDrh=0b+surQt!df6%6>?R>b>?TRTULX5|$Q3$9_AlXKSwKDCGRw-!v>4ZIeZlm5(KqA}ZCAqL8Gt zWKk&|CG|x5KvZHHMIIvY-uJDK_r3Squ5+FHoc}rhbFSmJ@ ze*gebasgmLR9@8<3-CoEi)kOiU;u`oj0Thu>VOg`A;1rSzylB(C;<)#?ccI1V);)S zBmkta0n|^MZQu$$FTpS5GqWm55PuLQ$Y0ioAc@j18SsVNl3N3$;4(YP&Wi^COV&YO zgw65Q0U(+e+nvd0GB#6!BO^$G+arTmBtb+J`U*n}T$Nn|oy2%93gEW%onG0B)n#SjPt3-0z1imNsKr#rZ_B!=<%Q4}&cCMJdy zV@itThLTsWU%#GgY(h3MSp{0G;_cz^0|l!%JiQr^UpUq*UNDy(#b-xy2oP>yP-HaU zl1PLC{eEWhc?wIEe0#)AC2Y*dVvU$+X_{78FiD|Z26puK#nw&#N6K#jtvgJpyiBOr#9 z^5pd2ni9TT!I!5M7x3CIR0vg`>TBjZ8cH1NzhO346>#s&HSxT z8C`E0J6!hTY&3SKg;e&kr8LIiR%x*7gimyfBNBnaHmEOO)=H%`FRL%l$Z6c5rEL^{ zdE?j{&#h{L2RD0nqX}0ZA}>(CpBuJYzqRuO(?*Xp!tU5B-0T23OCu0v8E=g~Tf6C8 z!yO)3;=f!rp^v2jqT7bXFAm5&VsGp_Ix0>mB zBZ!P_sydpGP%G42`01{{k*R8#^4y8D;=ggUuKCIi9dVxW_W?t#>ZUa698n47eU_L-aDGIx8aJn zMrZ0buko;klwb}DOY540mNpMC&b;3)?v?)|e%ogizvx|Ht_H;EJ~HV{)AxrpV?WYv zrGGoE)Ajw1?*4HokvQTP_vRB>Q#MCBiG{d0{mmMK>1PgtXlF2rzFTp=TDIEdV~MXa zq=nPP-Q_r%BnDBIwpganvc;G0+xG2I>#A`TFak}YzI^1JtNb&btf{@~{IM(Fi?#id zHNY|e$Ox)KSt zXBM(GEdN3rfT5;;JHX zHRjoHJaxOqOp^`$l?Eg;$Hp@U5wP+Cy4ogF)7-PpPpz;-YdIYNlP*FX^-H^K{_K4d zeUeByLIG0(MB79PDy2iL%8)Mww+K{~sv;~b({yhYF3x*Dq`)%$0{#k3mFvC`{!`nO zcee%V3%Ui9MbVx*=X(RpSZuHo1s$b6D*`)%NO5izzd9~X-2!o_L*09$;h`6=b1KIW z4kxK~x;iKt6U+3we*{8KG>N~c{^a1b2!}H9p-SQvJqVqzjxJ5@5W5x1wGtuPPYtk~ zx;GJ~yB5P9$7oVqq0@6yG`wSUovK9ohlUgLt<@k~Mu4fwzNKB%+r~^B`9n7mG&ZgV(glLxK`7|Td6GZs1nr@oFE_jAKnHspCM(l9}+qbaO-#^%+I?-$sq``|^d)xiJ?|W;^!4KU~ znue+_oW00q*=77tN*~YY5}>7>wI zepc|vjYq~E;)+?7mdb5VfHm{ck&Z9JV#Du!cFx`qZv~M73%@YDh4Z9Au326$Ytq`h z3Zh$^N87C{yG0yLn|#W7k`0}5nw`YGb!!p#a$k4Lr~aO|vE`n!ZZXszu5k!rdd|Z` zA0HhmGmBujpKni`_MjF!MOj1b&eaeUn0lPGIC1+mH@_4Y_f6L!GH@O|_-GO@Nv==h zj)$3a#h&`c`o_ZF>ZcbDH&%Q+`pW1TU9qKR#m%e@@tffCm|>*OIxA$dH~97GFOMxY z^wPR{%@wR$IO*!8=FfanPI^0CF{%E=LVe`-sD65iRcj zaD3RihiMN%d@_7Gkv{wW_1*nCBbcUckKsF9x0x42m;M8>fmO!IOx)Y*ZssySLtUeo z?5VqcmzF}K!UK~h57EsU++=H1HDv#R>TjDGTcQKQgn_(IYF9nF{lidD?ck1OW!i;|>vX4fnF2RE43CeS_ zglmJFd+i!kSPY4W%krSb@?^-&i|OVM?Dqacb^Ac*FaGTy*jmK4KALGfo>exy0-XZSQ@)- ziy?G;E5As74;a8TxSAN#K pFFVF)drBrP?5C@~+Kum}{N^uWSt1|6i|OL@@vW delta 1410 zcmV-|1%3MN6{ZW2B$4nGe*gh1r62!CU!*?Ei7Ywj(ZS-M0c_{BYh_mZ` z5`7Q@Tvgd6zwGLDOkiI@oB{l27(D=Am$s|tF>)YZb`a0HdKNlh;Oo|Q^`a{ za_*ZQ@L>pH;Op0Pe{~#vW4W`-=!l!>1Gou*Z%-RLcE?k9#E-jrA3_-Tb~VxG+|wB7 zW=tnr_7`3K5CGq{%(3s09;S^qaRni zE&ajRL7bhi5C*QhyHU{@j~T={6E=khj+Vl z#7*?wFaTw8EA-;*m%D6nJ`4bAY&;}rXb{iCTo|C%EH;dDhmIyZpaU4D?dtaW7ehMY zc?e;Ep_(|;e-#f2;wJ&XE>7iWZ4l3*-$M<+aI;u@TTyKgKMnxK@MSzv`>@VKI)HKH zF1uU#p{66Ag%Ac9OYXLt<%gOeHkJSw*XQv>O%QuX2QaRO`A6xDI9uqC0l?U9m3G9s zVSs7O1TnyVHG&vmzwVR<@lOH36pA}y@1p~lMsW}~e*wTGN|&#F3IHat%d3(A7=a)L z*fT*4uxF(~+y($sIMERU>{qKJ2H3CSj`*JdU<$<@@lgOUg%3s(0Q+_1V0eK2Dn1xK z-!|F?0AtFrZ`Ja(w*kPIK0kk}3F5y3fHA$vKT2oBD>)~2HZj2N+OBTrhuWQWZvucZ zELI7j>kd8PVhxgb44AI8& zA>GQ*V=#XU0ET#e_KTq%aWX3gsME%oZaD37F#PiXpvHquU*3%2+OBSObw2>ONH(t< zmjLfFnn+Fb5AFasUmF`n5-YomCZ2TlJ^9_TYKy}4#Ikt2yF z0N5rw;d#vuN;~2vEK>uxNX~>0qSE(;_ss%;^W+k>JcH+T3{V=x4+DTr+UVB_w=q!Z ze~h>Z0M5#Ja31j9#tuqn#Ge9yl|}UP;6;p}bTk1#-}&Y&c+~fImX0P)0^s^uboER0 z2ltHr66Mm4ID^=^62NtmxtAL=GI$p;q0%6J7nk$E?rrq@vgcjh>FRC(T%I=i9GX0Q z*OdlwzWVubOmFp{Uo8KgUHIK)WiU6(fA3aTfLiU^HS5Fjx`-=2hRx9X82##rDXR$p z&UFyehVZKZV5=#+;^WW_1AGqJXVD*ZxeCa971Dg|o~JQQ5YGePnrou(N=rJ{gBUl2 z4+4O#A-o>OtCb1|Q5UvC7+{Ne3=7WuT}gm%s+=_!y+Q=wI>}jg`^m-=+nVSPkAJxQyxI|WvaCh)i>6X9zFL8>AUS?!w QmH+?%07*qoM6N<$f)p{EtpET3 diff --git a/Sources/Models/MessageStyle.swift b/Sources/Models/MessageStyle.swift index fcdbbf60f..e60b9f973 100644 --- a/Sources/Models/MessageStyle.swift +++ b/Sources/Models/MessageStyle.swift @@ -44,12 +44,12 @@ public enum MessageStyle { case bottomRight internal var imageOrientation: UIImage.Orientation { - switch self { - case .bottomRight: return .up - case .bottomLeft: return .upMirrored - case .topLeft: return .down - case .topRight: return .downMirrored - } + switch self { + case .bottomRight: return .up + case .bottomLeft: return .upMirrored + case .topLeft: return .down + case .topRight: return .downMirrored + } } } From 6bb47d5af59539cc0de38d6f187eb8e2e336f3f0 Mon Sep 17 00:00:00 2001 From: Jakub Kaspar Date: Wed, 30 Oct 2024 07:49:27 +0100 Subject: [PATCH 03/12] Remove plugins that caused issues, migrate to swift 6 --- Example/ChatExample.xcodeproj/project.pbxproj | 14 ++--- Example/Sources/AppDelegate.swift | 4 +- .../BasicAudioController.swift | 3 +- Example/Sources/Models/MockSocket.swift | 1 + Package.swift | 51 ++-------------- .../SwiftLintCommandPlugin.swift | 54 ---------------- Plugins/LintPlugin/SwiftLintPlugin.swift | 41 ------------- .../SwiftFormatPlugin/SwiftFormatPlugin.swift | 61 ------------------- .../MessagesViewController+Menu.swift | 4 -- .../MessagesViewController+State.swift | 21 ++++--- .../Controllers/MessagesViewController.swift | 4 +- Sources/Extensions/Bundle+Extensions.swift | 4 +- Sources/Extensions/UIColor+Extensions.swift | 1 + Sources/Layout/CellSizeCalculator.swift | 1 + ...ssagesCollectionViewLayoutAttributes.swift | 32 +--------- Sources/Models/AccessoryPosition.swift | 2 +- Sources/Models/DetectorType.swift | 6 +- .../LocationMessageSnapshotOptions.swift | 1 + Sources/Models/MessageKitDateFormatter.swift | 2 +- Sources/Models/MessageStyle.swift | 12 ++-- Sources/Protocols/MessagesDataSource.swift | 1 + .../Protocols/MessagesDisplayDelegate.swift | 1 + .../Protocols/MessagesLayoutDelegate.swift | 1 + Sources/Views/TypingIndicator.swift | 34 +++++------ 24 files changed, 67 insertions(+), 289 deletions(-) delete mode 100644 Plugins/LintCommandPlugin/SwiftLintCommandPlugin.swift delete mode 100644 Plugins/LintPlugin/SwiftLintPlugin.swift delete mode 100644 Plugins/SwiftFormatPlugin/SwiftFormatPlugin.swift diff --git a/Example/ChatExample.xcodeproj/project.pbxproj b/Example/ChatExample.xcodeproj/project.pbxproj index 10cd6690b..49465a9fd 100644 --- a/Example/ChatExample.xcodeproj/project.pbxproj +++ b/Example/ChatExample.xcodeproj/project.pbxproj @@ -673,13 +673,13 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = "$(SRCROOT)/Sources/Resources/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.messagekit.ChatExample; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -688,6 +688,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = "$(SRCROOT)/Sources/Resources/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -696,7 +697,6 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; - SWIFT_VERSION = 5.0; }; name = Release; }; @@ -705,6 +705,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; INFOPLIST_FILE = Tests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -712,7 +713,6 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.hexedbits.ChatExampleTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ChatExample.app/ChatExample"; }; name = Debug; @@ -722,6 +722,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; INFOPLIST_FILE = Tests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -731,7 +732,6 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; - SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ChatExample.app/ChatExample"; }; name = Release; @@ -740,6 +740,7 @@ isa = XCBuildConfiguration; buildSettings = { INFOPLIST_FILE = UITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -747,7 +748,6 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.hexedbits.ChatExampleUITests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; TEST_TARGET_NAME = ChatExample; }; name = Debug; @@ -756,6 +756,7 @@ isa = XCBuildConfiguration; buildSettings = { INFOPLIST_FILE = UITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -765,7 +766,6 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; - SWIFT_VERSION = 5.0; TEST_TARGET_NAME = ChatExample; }; name = Release; @@ -817,7 +817,7 @@ repositoryURL = "https://github.com/onevcat/Kingfisher"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 5.15.8; + minimumVersion = 8.0.0; }; }; /* End XCRemoteSwiftPackageReference section */ diff --git a/Example/Sources/AppDelegate.swift b/Example/Sources/AppDelegate.swift index 3cb9f9d4a..604e3eb43 100644 --- a/Example/Sources/AppDelegate.swift +++ b/Example/Sources/AppDelegate.swift @@ -22,7 +22,7 @@ import UIKit -@UIApplicationMain +@main final internal class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? @@ -35,7 +35,7 @@ final internal class AppDelegate: UIResponder, UIApplicationDelegate { UIViewController() ] : [masterViewController] - splitViewController.preferredDisplayMode = .allVisible + splitViewController.preferredDisplayMode = UISplitViewController.DisplayMode.oneBesideSecondary masterViewController.navigationItem.largeTitleDisplayMode = .never window = UIWindow(frame: UIScreen.main.bounds) diff --git a/Example/Sources/AudioController/BasicAudioController.swift b/Example/Sources/AudioController/BasicAudioController.swift index 8bdd84d8b..9f239d3d8 100644 --- a/Example/Sources/AudioController/BasicAudioController.swift +++ b/Example/Sources/AudioController/BasicAudioController.swift @@ -42,7 +42,8 @@ public enum PlayerState { /// The `BasicAudioController` update UI for current audio cell that is playing a sound /// and also creates and manage an `AVAudioPlayer` states, play, pause and stop. -open class BasicAudioController: NSObject, AVAudioPlayerDelegate { +@MainActor +open class BasicAudioController: NSObject, @preconcurrency AVAudioPlayerDelegate { // MARK: Lifecycle // MARK: - Init Methods diff --git a/Example/Sources/Models/MockSocket.swift b/Example/Sources/Models/MockSocket.swift index d48cacd63..41c9f1b11 100644 --- a/Example/Sources/Models/MockSocket.swift +++ b/Example/Sources/Models/MockSocket.swift @@ -23,6 +23,7 @@ import MessageKit import UIKit +@MainActor final class MockSocket { // MARK: Lifecycle diff --git a/Package.swift b/Package.swift index b0e0f6747..ca0bcbd50 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.10 +// swift-tools-version:6.0 // MIT License // @@ -24,14 +24,12 @@ import PackageDescription let package = Package( name: "MessageKit", - platforms: [.iOS(.v13)], + platforms: [.iOS(.v14)], products: [ - .library(name: "MessageKit", targets: ["MessageKit"]), - .plugin(name: "LintPlugin", targets: ["LintPlugin"]), - .plugin(name: "SwiftFormatPlugin", targets: ["SwiftFormatPlugin"]), + .library(name: "MessageKit", targets: ["MessageKit"]) ], dependencies: [ - .package(url: "https://github.com/nathantannar4/InputBarAccessoryView", .upToNextMajor(from: "6.5.0")), + .package(url: "https://github.com/nathantannar4/InputBarAccessoryView", .upToNextMajor(from: "7.0.0")), ], targets: [ // MARK: - MessageKit @@ -44,45 +42,6 @@ let package = Package( swiftSettings: [SwiftSetting.define("IS_SPM")] ), .testTarget(name: "MessageKitTests", dependencies: ["MessageKit"]), - - // MARK: - Plugins - - .binaryTarget( - name: "LintBinary", - url: "https://github.com/realm/SwiftLint/releases/download/0.47.1/SwiftLintBinary-macos.artifactbundle.zip", - checksum: "82ef90b7d76b02e41edd73423687d9cedf0bb9849dcbedad8df3a461e5a7b555" - ), - .plugin( - name: "LintPlugin", - capability: .buildTool(), - dependencies: ["LintBinary"] - ), - .plugin( - name: "LintCommandPlugin", - capability: .command( - intent: .custom( - verb: "lint", - description: "Lint Swift source files" - ) - ), - dependencies: ["LintBinary"] - ), - - .binaryTarget( - name: "swiftformat", - url: "https://github.com/nicklockwood/SwiftFormat/releases/download/0.49.13/swiftformat.artifactbundle.zip", - checksum: "5ce27780dceee8714b15d53141e6dce1a8d626e970eade3c511c9ef1a0c06f40" - ), - .plugin( - name: "SwiftFormatPlugin", - capability: .command( - intent: .sourceCodeFormatting(), - permissions: [ - .writeToPackageDirectory(reason: "Format Swift source files"), - ] - ), - dependencies: ["swiftformat"] - ), ], - swiftLanguageVersions: [.v5] + swiftLanguageModes: [.v6] ) diff --git a/Plugins/LintCommandPlugin/SwiftLintCommandPlugin.swift b/Plugins/LintCommandPlugin/SwiftLintCommandPlugin.swift deleted file mode 100644 index c75bcf17c..000000000 --- a/Plugins/LintCommandPlugin/SwiftLintCommandPlugin.swift +++ /dev/null @@ -1,54 +0,0 @@ -// MIT License -// -// Copyright (c) 2017-2022 MessageKit -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -import Foundation -import PackagePlugin - -// MARK: - SwiftLintCommandPlugin - -@main -struct SwiftLintCommandPlugin: CommandPlugin { - func performCommand(context: PackagePlugin.PluginContext, arguments _: [String]) async throws { - let swiftLintTool = try context.tool(named: "swiftlint") - let swiftLintPath = URL(fileURLWithPath: swiftLintTool.path.string) - - let swiftLintArgs = [ - "lint", - "--path", context.package.directory.string, - "--config", context.package.directory.string + "/.swiftlint.yml", - "--strict", - ] - - let task = try Process.run(swiftLintPath, arguments: swiftLintArgs) - task.waitUntilExit() - - if task.terminationStatus == 0 || task.terminationStatus == 2 { - // no-op - } else { - throw CommandError.unknownError(exitCode: task.terminationStatus) - } - } -} - -// MARK: - CommandError - -enum CommandError: Error { - case unknownError(exitCode: Int32) -} diff --git a/Plugins/LintPlugin/SwiftLintPlugin.swift b/Plugins/LintPlugin/SwiftLintPlugin.swift deleted file mode 100644 index e88b10abd..000000000 --- a/Plugins/LintPlugin/SwiftLintPlugin.swift +++ /dev/null @@ -1,41 +0,0 @@ -// MIT License -// -// Copyright (c) 2017-2022 MessageKit -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -import PackagePlugin - -@main -struct LintPlugin: BuildToolPlugin { - func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] { - [ - .buildCommand( - displayName: "Linting \(target.name)", - executable: try context.tool(named: "swiftlint").path, - arguments: [ - "lint", - "--in-process-sourcekit", - "--path", - target.directory.string, - "--config", - "\(context.package.directory.string)/.swiftlint.yml", - ], - environment: [:]), - ] - } -} diff --git a/Plugins/SwiftFormatPlugin/SwiftFormatPlugin.swift b/Plugins/SwiftFormatPlugin/SwiftFormatPlugin.swift deleted file mode 100644 index 34b1d70de..000000000 --- a/Plugins/SwiftFormatPlugin/SwiftFormatPlugin.swift +++ /dev/null @@ -1,61 +0,0 @@ -// MIT License -// -// Copyright (c) 2017-2022 MessageKit -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -import Foundation -import PackagePlugin - -// MARK: - SwiftFormatPlugin - -@main -struct SwiftFormatPlugin: CommandPlugin { - func performCommand(context: PackagePlugin.PluginContext, arguments: [String]) async throws { - var argumentExtractor = ArgumentExtractor(arguments) - let files = argumentExtractor.extractOption(named: "file") - - let process = Process() - process.launchPath = try context.tool(named: "swiftformat").path.string - process.arguments = [ - files.first!, - "--swiftversion", - "5.6", - "--cache", - context.pluginWorkDirectory.string + "/swiftformat.cache", - ] - - try process.run() - process.waitUntilExit() - - switch process.terminationStatus { - case EXIT_SUCCESS: - break - case EXIT_FAILURE: - throw CommandError.lintFailure - default: - throw CommandError.unknownError(exitCode: process.terminationStatus) - } - } -} - -// MARK: - CommandError - -enum CommandError: Error { - case lintFailure - case unknownError(exitCode: Int32) -} diff --git a/Sources/Controllers/MessagesViewController+Menu.swift b/Sources/Controllers/MessagesViewController+Menu.swift index d454d3fc2..7aa07a36d 100644 --- a/Sources/Controllers/MessagesViewController+Menu.swift +++ b/Sources/Controllers/MessagesViewController+Menu.swift @@ -36,10 +36,6 @@ extension MessagesViewController { object: nil) } - internal func removeMenuControllerObservers() { - NotificationCenter.default.removeObserver(self, name: UIMenuController.willShowMenuNotification, object: nil) - } - // MARK: Private // MARK: - Helpers diff --git a/Sources/Controllers/MessagesViewController+State.swift b/Sources/Controllers/MessagesViewController+State.swift index d72c3d186..62d2d9cdb 100644 --- a/Sources/Controllers/MessagesViewController+State.swift +++ b/Sources/Controllers/MessagesViewController+State.swift @@ -26,17 +26,18 @@ import InputBarAccessoryView import UIKit extension MessagesViewController { - final class State { - /// Pan gesture for display the date of message by swiping left. - var panGesture: UIPanGestureRecognizer? - var maintainPositionOnInputBarHeightChanged = false - var scrollsToLastItemOnKeyboardBeginsEditing = false + @MainActor + final class State { + /// Pan gesture for display the date of message by swiping left. + var panGesture: UIPanGestureRecognizer? + var maintainPositionOnInputBarHeightChanged = false + var scrollsToLastItemOnKeyboardBeginsEditing = false - let inputContainerView: MessagesInputContainerView = .init() - @Published var inputBarType: MessageInputBarKind = .messageInputBar - let keyboardManager = KeyboardManager() - var disposeBag: Set = .init() - } + let inputContainerView: MessagesInputContainerView = .init() + @Published var inputBarType: MessageInputBarKind = .messageInputBar + let keyboardManager = KeyboardManager() + var disposeBag: Set = .init() + } // MARK: - Getters diff --git a/Sources/Controllers/MessagesViewController.swift b/Sources/Controllers/MessagesViewController.swift index 1046517d5..41965d5a4 100644 --- a/Sources/Controllers/MessagesViewController.swift +++ b/Sources/Controllers/MessagesViewController.swift @@ -31,8 +31,8 @@ open class MessagesViewController: UIViewController, UICollectionViewDelegateFlo // MARK: Lifecycle deinit { - removeMenuControllerObservers() - clearMemoryCache() + NotificationCenter.default.removeObserver(self, name: UIMenuController.willShowMenuNotification, object: nil) + MessageStyle.bubbleImageCache.removeAllObjects() } // MARK: Open diff --git a/Sources/Extensions/Bundle+Extensions.swift b/Sources/Extensions/Bundle+Extensions.swift index bc88f309e..aa4ca7d4f 100644 --- a/Sources/Extensions/Bundle+Extensions.swift +++ b/Sources/Extensions/Bundle+Extensions.swift @@ -24,9 +24,9 @@ import Foundation extension Bundle { #if IS_SPM - internal static var messageKitAssetBundle = Bundle.module + nonisolated internal static let messageKitAssetBundle = Bundle.module #else - internal static var messageKitAssetBundle: Bundle { + nonisolated internal static var messageKitAssetBundle: Bundle { Bundle(for: MessagesViewController.self) } #endif diff --git a/Sources/Extensions/UIColor+Extensions.swift b/Sources/Extensions/UIColor+Extensions.swift index b40233cef..e5df7ad80 100644 --- a/Sources/Extensions/UIColor+Extensions.swift +++ b/Sources/Extensions/UIColor+Extensions.swift @@ -23,6 +23,7 @@ import Foundation import UIKit +@MainActor extension UIColor { // MARK: Internal diff --git a/Sources/Layout/CellSizeCalculator.swift b/Sources/Layout/CellSizeCalculator.swift index 6c153ceb9..85ffbffed 100644 --- a/Sources/Layout/CellSizeCalculator.swift +++ b/Sources/Layout/CellSizeCalculator.swift @@ -24,6 +24,7 @@ import UIKit /// An object is responsible for /// sizing and configuring cells for given `IndexPath`s. +@MainActor open class CellSizeCalculator { // MARK: Lifecycle diff --git a/Sources/Layout/MessagesCollectionViewLayoutAttributes.swift b/Sources/Layout/MessagesCollectionViewLayoutAttributes.swift index d6d69e12c..ff8fefad6 100644 --- a/Sources/Layout/MessagesCollectionViewLayoutAttributes.swift +++ b/Sources/Layout/MessagesCollectionViewLayoutAttributes.swift @@ -23,7 +23,7 @@ import UIKit /// The layout attributes used by a `MessageCollectionViewCell` to layout its subviews. -open class MessagesCollectionViewLayoutAttributes: UICollectionViewLayoutAttributes { +open class MessagesCollectionViewLayoutAttributes: UICollectionViewLayoutAttributes { // MARK: Open // MARK: - Methods @@ -55,36 +55,6 @@ open class MessagesCollectionViewLayoutAttributes: UICollectionViewLayoutAttribu // swiftlint:enable force_cast } - open override func isEqual(_ object: Any?) -> Bool { - // MARK: - LEAVE this as is - if let attributes = object as? MessagesCollectionViewLayoutAttributes { - return super.isEqual(object) && attributes.avatarSize == avatarSize - && attributes.avatarPosition == avatarPosition - && attributes.avatarLeadingTrailingPadding == avatarLeadingTrailingPadding - && attributes.messageContainerSize == messageContainerSize - && attributes.messageContainerPadding == messageContainerPadding - && attributes.messageLabelFont == messageLabelFont - && attributes.messageLabelInsets == messageLabelInsets - && attributes.cellTopLabelAlignment == cellTopLabelAlignment - && attributes.cellTopLabelSize == cellTopLabelSize - && attributes.cellBottomLabelAlignment == cellBottomLabelAlignment - && attributes.cellBottomLabelSize == cellBottomLabelSize - && attributes.messageTimeLabelSize == messageTimeLabelSize - && attributes.messageTopLabelAlignment == messageTopLabelAlignment - && attributes.messageTopLabelSize == messageTopLabelSize - && attributes.messageBottomLabelAlignment == messageBottomLabelAlignment - && attributes.messageBottomLabelSize == messageBottomLabelSize - && attributes.accessoryViewSize == accessoryViewSize - && attributes.accessoryViewPadding == accessoryViewPadding - && attributes.accessoryViewPosition == accessoryViewPosition - && attributes.linkPreviewFonts == linkPreviewFonts - } else { - return false - } - } - - // MARK: Public - // MARK: - Properties public var avatarSize: CGSize = .zero diff --git a/Sources/Models/AccessoryPosition.swift b/Sources/Models/AccessoryPosition.swift index 3fd69bb3a..59c09c5ce 100644 --- a/Sources/Models/AccessoryPosition.swift +++ b/Sources/Models/AccessoryPosition.swift @@ -24,7 +24,7 @@ import Foundation /// Used to determine the `Horizontal` and `Vertical` position of /// an `AccessoryView` in a `MessageCollectionViewCell`. -public enum AccessoryPosition { +public enum AccessoryPosition: Equatable { /// Aligns the `AccessoryView`'s top edge to the cell's top edge. case cellTop diff --git a/Sources/Models/DetectorType.swift b/Sources/Models/DetectorType.swift index d2dd83e9a..7fd0cb228 100644 --- a/Sources/Models/DetectorType.swift +++ b/Sources/Models/DetectorType.swift @@ -22,7 +22,7 @@ import Foundation -public enum DetectorType: Hashable { +public enum DetectorType: Hashable, Sendable { case address case date case phoneNumber @@ -33,8 +33,8 @@ public enum DetectorType: Hashable { // MARK: Public // swiftlint:disable force_try - public static var hashtag = DetectorType.custom(try! NSRegularExpression(pattern: "#[a-zA-Z0-9]{4,}", options: [])) - public static var mention = DetectorType.custom(try! NSRegularExpression(pattern: "@[a-zA-Z0-9]{4,}", options: [])) + public static let hashtag = DetectorType.custom(try! NSRegularExpression(pattern: "#[a-zA-Z0-9]{4,}", options: [])) + public static let mention = DetectorType.custom(try! NSRegularExpression(pattern: "@[a-zA-Z0-9]{4,}", options: [])) /// Simply check if the detector type is a .custom public var isCustom: Bool { diff --git a/Sources/Models/LocationMessageSnapshotOptions.swift b/Sources/Models/LocationMessageSnapshotOptions.swift index bed668432..792a07310 100644 --- a/Sources/Models/LocationMessageSnapshotOptions.swift +++ b/Sources/Models/LocationMessageSnapshotOptions.swift @@ -23,6 +23,7 @@ import MapKit /// An object grouping the settings used by the `MKMapSnapshotter` through the `LocationMessageDisplayDelegate`. +@MainActor public struct LocationMessageSnapshotOptions { // MARK: Lifecycle diff --git a/Sources/Models/MessageKitDateFormatter.swift b/Sources/Models/MessageKitDateFormatter.swift index 7d39a9fa8..c3636034e 100644 --- a/Sources/Models/MessageKitDateFormatter.swift +++ b/Sources/Models/MessageKitDateFormatter.swift @@ -22,7 +22,7 @@ import Foundation -open class MessageKitDateFormatter { +open class MessageKitDateFormatter: @unchecked Sendable { // MARK: Lifecycle // MARK: - Initializer diff --git a/Sources/Models/MessageStyle.swift b/Sources/Models/MessageStyle.swift index e60b9f973..ed1dbeba4 100644 --- a/Sources/Models/MessageStyle.swift +++ b/Sources/Models/MessageStyle.swift @@ -111,13 +111,13 @@ public enum MessageStyle { return strechAndCache(image: image) } - // MARK: Internal + // MARK: Internal - internal static let bubbleImageCache: NSCache = { - let cache = NSCache() - cache.name = "com.messagekit.MessageKit.bubbleImageCache" - return cache - }() + nonisolated(unsafe) internal static let bubbleImageCache: NSCache = { + let cache = NSCache() + cache.name = "com.messagekit.MessageKit.bubbleImageCache" + return cache + }() // MARK: Private diff --git a/Sources/Protocols/MessagesDataSource.swift b/Sources/Protocols/MessagesDataSource.swift index 7729d898b..831d1cf2f 100644 --- a/Sources/Protocols/MessagesDataSource.swift +++ b/Sources/Protocols/MessagesDataSource.swift @@ -26,6 +26,7 @@ import UIKit /// An object that adopts the `MessagesDataSource` protocol is responsible for providing /// the data required by a `MessagesCollectionView`. +@MainActor public protocol MessagesDataSource: AnyObject { /// The `SenderType` of new messages in the `MessagesCollectionView`. var currentSender: SenderType { get } diff --git a/Sources/Protocols/MessagesDisplayDelegate.swift b/Sources/Protocols/MessagesDisplayDelegate.swift index 2548fde8f..81a5d6722 100644 --- a/Sources/Protocols/MessagesDisplayDelegate.swift +++ b/Sources/Protocols/MessagesDisplayDelegate.swift @@ -27,6 +27,7 @@ import UIKit // MARK: - MessagesDisplayDelegate /// A protocol used by the `MessagesViewController` to customize the appearance of a `MessageContentCell`. +@MainActor public protocol MessagesDisplayDelegate: AnyObject { // MARK: - All Messages diff --git a/Sources/Protocols/MessagesLayoutDelegate.swift b/Sources/Protocols/MessagesLayoutDelegate.swift index b6fafe1c3..015c45c30 100644 --- a/Sources/Protocols/MessagesLayoutDelegate.swift +++ b/Sources/Protocols/MessagesLayoutDelegate.swift @@ -27,6 +27,7 @@ import UIKit /// A protocol used by the `MessagesCollectionViewFlowLayout` object to determine /// the size and layout of a `MessageCollectionViewCell` and its contents. +@MainActor public protocol MessagesLayoutDelegate: AnyObject { /// Specifies the size to use for a header view. /// diff --git a/Sources/Views/TypingIndicator.swift b/Sources/Views/TypingIndicator.swift index 208ce337c..f0eb73d3c 100644 --- a/Sources/Views/TypingIndicator.swift +++ b/Sources/Views/TypingIndicator.swift @@ -87,23 +87,23 @@ open class TypingIndicator: UIView { /// Sets the state of the `TypingIndicator` to animating and applies animation layers open func startAnimating() { - defer { isAnimating = true } - guard !isAnimating else { return } - var delay: TimeInterval = 0 - for dot in dots { - DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in - guard let self = self else { return } - if self.isBounceEnabled { - dot.layer.add(self.initialOffsetAnimationLayer, forKey: AnimationKeys.offset) - let bounceLayer = self.bounceAnimationLayer - bounceLayer.timeOffset = delay + 0.33 - dot.layer.add(bounceLayer, forKey: AnimationKeys.bounce) - } - if self.isFadeEnabled { - dot.layer.add(self.opacityAnimationLayer, forKey: AnimationKeys.opacity) - } - } - delay += 0.33 + defer { isAnimating = true } + guard !isAnimating else { return } + var delay: TimeInterval = 0 + for dot in dots { + DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in + guard let self = self else { return } + if self.isBounceEnabled { + dot.layer.add(self.initialOffsetAnimationLayer, forKey: AnimationKeys.offset) + let bounceLayer = self.bounceAnimationLayer + bounceLayer.timeOffset = delay + 0.33 + dot.layer.add(bounceLayer, forKey: AnimationKeys.bounce) + } + if self.isFadeEnabled { + dot.layer.add(self.opacityAnimationLayer, forKey: AnimationKeys.opacity) + } + } + delay += 0.33 } } From 84ca1504be1244629c55f7e7ac901365773f4bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ka=C5=A1par?= Date: Wed, 30 Oct 2024 07:55:56 +0100 Subject: [PATCH 04/12] Update ci_pr_example.yml --- .github/workflows/ci_pr_example.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_pr_example.yml b/.github/workflows/ci_pr_example.yml index 437310337..d98c63fda 100644 --- a/.github/workflows/ci_pr_example.yml +++ b/.github/workflows/ci_pr_example.yml @@ -10,7 +10,7 @@ concurrency: jobs: tests: name: Build Example app - runs-on: macos-14 + runs-on: macos-15 steps: - name: Checkout the Git repository uses: actions/checkout@v4 From 048f5391905d91c19f19f426c313a8dba8298696 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ka=C5=A1par?= Date: Wed, 30 Oct 2024 07:56:03 +0100 Subject: [PATCH 05/12] Update ci_pr_framework.yml --- .github/workflows/ci_pr_framework.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_pr_framework.yml b/.github/workflows/ci_pr_framework.yml index da5365c13..08bd9b031 100644 --- a/.github/workflows/ci_pr_framework.yml +++ b/.github/workflows/ci_pr_framework.yml @@ -10,7 +10,7 @@ concurrency: jobs: tests: name: Build Framework - runs-on: macos-14 + runs-on: macos-15 steps: - name: Checkout the Git repository uses: actions/checkout@v4 From f16daea7e12279ed9108ae76d7a8c3c3cf5c6544 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ka=C5=A1par?= Date: Wed, 30 Oct 2024 07:56:11 +0100 Subject: [PATCH 06/12] Update ci_pr_tests.yml --- .github/workflows/ci_pr_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_pr_tests.yml b/.github/workflows/ci_pr_tests.yml index 0c1058452..cd53f5472 100644 --- a/.github/workflows/ci_pr_tests.yml +++ b/.github/workflows/ci_pr_tests.yml @@ -10,7 +10,7 @@ concurrency: jobs: tests: name: Run Tests - runs-on: macos-14 + runs-on: macos-15 steps: - name: Checkout the Git repository uses: actions/checkout@v4 From 7e4c11639edf0c03a9e2c8aa3b791be5d72b8466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ka=C5=A1par?= Date: Sat, 2 Nov 2024 16:28:21 +0100 Subject: [PATCH 07/12] Update Makefile --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 87ab6715a..56e05c78f 100644 --- a/Makefile +++ b/Makefile @@ -25,15 +25,15 @@ test: @echo "Running MessageKit tests." - @set -o pipefail && xcodebuild test -scheme MessageKit -sdk iphonesimulator -destination "platform=iOS Simulator,name=iPhone 15" | xcpretty -c + @set -o pipefail && xcodebuild test -scheme MessageKit -sdk iphonesimulator -destination "platform=iOS Simulator,name=iPhone 16" | xcpretty -c framework: @echo "Building MessageKit Framework." - @set -o pipefail && xcodebuild build -scheme MessageKit -destination "platform=iOS Simulator,name=iPhone 15" | xcpretty -c + @set -o pipefail && xcodebuild build -scheme MessageKit -destination "platform=iOS Simulator,name=iPhone 16" | xcpretty -c build_example: @echo "Building & testing MessageKit Example app." - @cd Example && set -o pipefail && xcodebuild build analyze -scheme ChatExample -destination "platform=iOS Simulator,name=iPhone 15" CODE_SIGNING_REQUIRED=NO | xcpretty -c + @cd Example && set -o pipefail && xcodebuild build analyze -scheme ChatExample -destination "platform=iOS Simulator,name=iPhone 16" CODE_SIGNING_REQUIRED=NO | xcpretty -c format: @swift package --allow-writing-to-package-directory format-source-code --file . From 644ee724180ec6fc4a9d7bb4147e71a0b7670aac Mon Sep 17 00:00:00 2001 From: Jakub Kaspar Date: Mon, 25 Nov 2024 17:39:15 +0100 Subject: [PATCH 08/12] Fix tests and warnings --- Sources/Views/TypingIndicator.swift | 3 +- .../Controllers Test/MessageLabelTests.swift | 1 + .../MessagesViewControllerTests.swift | 498 +++++++++--------- Tests/MessageKitTests/MessageKitTests.swift | 6 +- .../Mocks/MockMessagesDataSource.swift | 35 +- .../MessagesDisplayDelegateTests.swift | 379 +++++++------ .../Views Tests/AvatarViewTests.swift | 105 ++-- .../MessageCollectionViewCellTests.swift | 90 ++-- .../MessagesCollectionViewTests.swift | 27 +- 9 files changed, 567 insertions(+), 577 deletions(-) diff --git a/Sources/Views/TypingIndicator.swift b/Sources/Views/TypingIndicator.swift index f0eb73d3c..355600987 100644 --- a/Sources/Views/TypingIndicator.swift +++ b/Sources/Views/TypingIndicator.swift @@ -91,12 +91,13 @@ open class TypingIndicator: UIView { guard !isAnimating else { return } var delay: TimeInterval = 0 for dot in dots { + let currentDelay = delay DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in guard let self = self else { return } if self.isBounceEnabled { dot.layer.add(self.initialOffsetAnimationLayer, forKey: AnimationKeys.offset) let bounceLayer = self.bounceAnimationLayer - bounceLayer.timeOffset = delay + 0.33 + bounceLayer.timeOffset = currentDelay + 0.33 dot.layer.add(bounceLayer, forKey: AnimationKeys.bounce) } if self.isFadeEnabled { diff --git a/Tests/MessageKitTests/Controllers Test/MessageLabelTests.swift b/Tests/MessageKitTests/Controllers Test/MessageLabelTests.swift index 4d1264768..78cae636b 100644 --- a/Tests/MessageKitTests/Controllers Test/MessageLabelTests.swift +++ b/Tests/MessageKitTests/Controllers Test/MessageLabelTests.swift @@ -26,6 +26,7 @@ import XCTest // MARK: - MessageLabelTests +@MainActor final class MessageLabelTests: XCTestCase { // MARK: Internal diff --git a/Tests/MessageKitTests/Controllers Test/MessagesViewControllerTests.swift b/Tests/MessageKitTests/Controllers Test/MessagesViewControllerTests.swift index 858764783..417bcea11 100644 --- a/Tests/MessageKitTests/Controllers Test/MessagesViewControllerTests.swift +++ b/Tests/MessageKitTests/Controllers Test/MessagesViewControllerTests.swift @@ -26,264 +26,264 @@ import XCTest // MARK: - MessagesViewControllerTests +@MainActor final class MessagesViewControllerTests: XCTestCase { - // MARK: Internal - var sut: MessagesViewController! - - // swiftlint:enable weak_delegate - - // MARK: - Overridden Methods - - override func setUp() { - super.setUp() - - sut = MessagesViewController() - sut.messagesCollectionView.messagesLayoutDelegate = layoutDelegate - sut.messagesCollectionView.messagesDisplayDelegate = layoutDelegate - _ = sut.view - sut.beginAppearanceTransition(true, animated: true) - sut.endAppearanceTransition() - sut.view.layoutIfNeeded() - } - - override func tearDown() { - sut = nil - - super.tearDown() - } - - // MARK: - Test - - func testNumberOfSectionWithoutData_isZero() { - let messagesDataSource = MockMessagesDataSource() - sut.messagesCollectionView.messagesDataSource = messagesDataSource - - XCTAssertEqual(sut.messagesCollectionView.numberOfSections, 0) - } - - func testNumberOfSection_isNumberOfMessages() { - let messagesDataSource = MockMessagesDataSource() - sut.messagesCollectionView.messagesDataSource = messagesDataSource - messagesDataSource.messages = makeMessages(for: messagesDataSource.senders) - - sut.messagesCollectionView.reloadData() - - let count = sut.messagesCollectionView.numberOfSections - let expectedCount = messagesDataSource.numberOfSections(in: sut.messagesCollectionView) - - XCTAssertEqual(count, expectedCount) - } - - func testNumberOfItemInSection_isOne() { - let messagesDataSource = MockMessagesDataSource() - sut.messagesCollectionView.messagesDataSource = messagesDataSource - messagesDataSource.messages = makeMessages(for: messagesDataSource.senders) - - sut.messagesCollectionView.reloadData() - - XCTAssertEqual(sut.messagesCollectionView.numberOfItems(inSection: 0), 1) - XCTAssertEqual(sut.messagesCollectionView.numberOfItems(inSection: 1), 1) - } - - func testCellForItemWithTextData_returnsTextMessageCell() { - let messagesDataSource = MockMessagesDataSource() - sut.messagesCollectionView.messagesDataSource = messagesDataSource - messagesDataSource.messages.append(MockMessage( - text: "Test", - user: messagesDataSource.senders[0], - messageId: "test_id")) - - sut.messagesCollectionView.reloadData() - - let cell = sut.messagesCollectionView.dataSource?.collectionView( - sut.messagesCollectionView, - cellForItemAt: IndexPath(item: 0, section: 0)) - - XCTAssertNotNil(cell) - XCTAssertTrue(cell is TextMessageCell) - } - - func testCellForItemWithAttributedTextData_returnsTextMessageCell() { - let messagesDataSource = MockMessagesDataSource() - sut.messagesCollectionView.messagesDataSource = messagesDataSource - let attributes = [NSAttributedString.Key.foregroundColor: UIColor.outgoingMessageLabel] - let attriutedString = NSAttributedString(string: "Test", attributes: attributes) - messagesDataSource.messages.append(MockMessage( - attributedText: attriutedString, - user: messagesDataSource.senders[0], - messageId: "test_id")) - - sut.messagesCollectionView.reloadData() - - let cell = sut.messagesCollectionView.dataSource?.collectionView( - sut.messagesCollectionView, - cellForItemAt: IndexPath(item: 0, section: 0)) - - XCTAssertNotNil(cell) - XCTAssertTrue(cell is TextMessageCell) - } - - func testCellForItemWithPhotoData_returnsMediaMessageCell() { - let messagesDataSource = MockMessagesDataSource() - sut.messagesCollectionView.messagesDataSource = messagesDataSource - messagesDataSource.messages.append(MockMessage( - image: UIImage(), - user: messagesDataSource.senders[0], - messageId: "test_id")) - - sut.messagesCollectionView.reloadData() - - let cell = sut.messagesCollectionView.dataSource?.collectionView( - sut.messagesCollectionView, - cellForItemAt: IndexPath(item: 0, section: 0)) - - XCTAssertNotNil(cell) - XCTAssertTrue(cell is MediaMessageCell) - } - - func testCellForItemWithVideoData_returnsMediaMessageCell() { - let messagesDataSource = MockMessagesDataSource() - sut.messagesCollectionView.messagesDataSource = messagesDataSource - messagesDataSource.messages.append(MockMessage( - thumbnail: UIImage(), - user: messagesDataSource.senders[0], - messageId: "test_id")) - - sut.messagesCollectionView.reloadData() - - let cell = sut.messagesCollectionView.dataSource?.collectionView( - sut.messagesCollectionView, - cellForItemAt: IndexPath(item: 0, section: 0)) - - XCTAssertNotNil(cell) - XCTAssertTrue(cell is MediaMessageCell) - } - - func testCellForItemWithLocationData_returnsLocationMessageCell() { - let messagesDataSource = MockMessagesDataSource() - sut.messagesCollectionView.messagesDataSource = messagesDataSource - messagesDataSource.messages.append(MockMessage( - location: CLLocation(latitude: 60.0, longitude: 70.0), - user: messagesDataSource.senders[0], - messageId: "test_id")) - - sut.messagesCollectionView.reloadData() - - let cell = sut.messagesCollectionView.dataSource?.collectionView( - sut.messagesCollectionView, - cellForItemAt: IndexPath(item: 0, section: 0)) - - XCTAssertNotNil(cell) - XCTAssertTrue(cell is LocationMessageCell) - } - - func testCellForItemWithAudioData_returnsAudioMessageCell() { - let messagesDataSource = MockMessagesDataSource() - sut.messagesCollectionView.messagesDataSource = messagesDataSource - messagesDataSource.messages.append(MockMessage( - audioURL: URL(fileURLWithPath: ""), - duration: 4.0, - user: messagesDataSource.senders[0], - messageId: "test_id")) - - sut.messagesCollectionView.reloadData() - - let cell = sut.messagesCollectionView.dataSource?.collectionView( - sut.messagesCollectionView, - cellForItemAt: IndexPath(item: 0, section: 0)) - - XCTAssertNotNil(cell) - XCTAssertTrue(cell is AudioMessageCell) - } - - func testCellForItemWithLinkPreviewData_returnsLinkPreviewMessageCell() { - let messagesDataSource = MockMessagesDataSource() - sut.messagesCollectionView.messagesDataSource = messagesDataSource - - let linkItem = MockLinkItem( - text: "https://link.test", - attributedText: nil, - url: URL(string: "https://github.com/MessageKit")!, - title: "Link Title", - teaser: "Link Teaser", - thumbnailImage: UIImage()) - - messagesDataSource.messages.append(MockMessage( - linkItem: linkItem, - user: messagesDataSource.senders[0], - messageId: "test_id")) - - sut.messagesCollectionView.reloadData() - - let cell = sut.messagesCollectionView.dataSource?.collectionView( - sut.messagesCollectionView, - cellForItemAt: IndexPath(item: 0, section: 0)) - - XCTAssertNotNil(cell) - XCTAssertTrue(cell is LinkPreviewMessageCell) - } - - // MARK: - Setups - - func testSubviewsSetup() { - let controller = MessagesViewController() - XCTAssertTrue(controller.view.subviews.contains(controller.messagesCollectionView)) - } - - func testDelegateAndDataSourceSetup() { - let controller = MessagesViewController() - controller.view.layoutIfNeeded() - XCTAssertTrue(controller.messagesCollectionView.delegate is MessagesViewController) - XCTAssertTrue(controller.messagesCollectionView.dataSource is MessagesViewController) - } - - func testDefaultPropertyValues() { - let controller = MessagesViewController() - XCTAssertNotNil(controller.messagesCollectionView) - XCTAssertTrue(controller.messagesCollectionView.collectionViewLayout is MessagesCollectionViewFlowLayout) - - controller.view.layoutIfNeeded() - XCTAssertTrue(controller.extendedLayoutIncludesOpaqueBars) - XCTAssertEqual(controller.view.backgroundColor, UIColor.collectionViewBackground) - XCTAssertEqual(controller.messagesCollectionView.keyboardDismissMode, UIScrollView.KeyboardDismissMode.interactive) - XCTAssertTrue(controller.messagesCollectionView.alwaysBounceVertical) - } - - // MARK: Private - - // swiftlint:disable weak_delegate - private var layoutDelegate = MockLayoutDelegate() - - // MARK: - Assistants - - private func makeMessages(for senders: [MockUser]) -> [MessageType] { - [ - MockMessage(text: "Text 1", user: senders[0], messageId: "test_id_1"), - MockMessage(text: "Text 2", user: senders[1], messageId: "test_id_2"), - ] - } + // MARK: - Private helper API + + private func makeSUT() -> MessagesViewController { + let sut = MessagesViewController() + sut.messagesCollectionView.messagesLayoutDelegate = layoutDelegate + sut.messagesCollectionView.messagesDisplayDelegate = layoutDelegate + _ = sut.view + sut.beginAppearanceTransition(true, animated: true) + sut.endAppearanceTransition() + sut.view.layoutIfNeeded() + + return sut + } + + // MARK: - Test + + func testNumberOfSectionWithoutData_isZero() { + let messagesDataSource = MockMessagesDataSource() + let sut = makeSUT() + sut.messagesCollectionView.messagesDataSource = messagesDataSource + + XCTAssertEqual(sut.messagesCollectionView.numberOfSections, 0) + } + + func testNumberOfSection_isNumberOfMessages() { + let messagesDataSource = MockMessagesDataSource() + let sut = makeSUT() + sut.messagesCollectionView.messagesDataSource = messagesDataSource + messagesDataSource.messages = makeMessages(for: messagesDataSource.senders) + + sut.messagesCollectionView.reloadData() + + let count = sut.messagesCollectionView.numberOfSections + let expectedCount = messagesDataSource.numberOfSections(in: sut.messagesCollectionView) + + XCTAssertEqual(count, expectedCount) + } + + func testNumberOfItemInSection_isOne() { + let messagesDataSource = MockMessagesDataSource() + let sut = makeSUT() + sut.messagesCollectionView.messagesDataSource = messagesDataSource + messagesDataSource.messages = makeMessages(for: messagesDataSource.senders) + + sut.messagesCollectionView.reloadData() + + XCTAssertEqual(sut.messagesCollectionView.numberOfItems(inSection: 0), 1) + XCTAssertEqual(sut.messagesCollectionView.numberOfItems(inSection: 1), 1) + } + + func testCellForItemWithTextData_returnsTextMessageCell() { + let messagesDataSource = MockMessagesDataSource() + let sut = makeSUT() + sut.messagesCollectionView.messagesDataSource = messagesDataSource + messagesDataSource.messages.append(MockMessage( + text: "Test", + user: messagesDataSource.senders[0], + messageId: "test_id")) + + sut.messagesCollectionView.reloadData() + + let cell = sut.messagesCollectionView.dataSource?.collectionView( + sut.messagesCollectionView, + cellForItemAt: IndexPath(item: 0, section: 0)) + + XCTAssertNotNil(cell) + XCTAssertTrue(cell is TextMessageCell) + } + + func testCellForItemWithAttributedTextData_returnsTextMessageCell() { + let messagesDataSource = MockMessagesDataSource() + let sut = makeSUT() + sut.messagesCollectionView.messagesDataSource = messagesDataSource + let attributes = [NSAttributedString.Key.foregroundColor: UIColor.outgoingMessageLabel] + let attriutedString = NSAttributedString(string: "Test", attributes: attributes) + messagesDataSource.messages.append(MockMessage( + attributedText: attriutedString, + user: messagesDataSource.senders[0], + messageId: "test_id")) + + sut.messagesCollectionView.reloadData() + + let cell = sut.messagesCollectionView.dataSource?.collectionView( + sut.messagesCollectionView, + cellForItemAt: IndexPath(item: 0, section: 0)) + + XCTAssertNotNil(cell) + XCTAssertTrue(cell is TextMessageCell) + } + + func testCellForItemWithPhotoData_returnsMediaMessageCell() { + let messagesDataSource = MockMessagesDataSource() + let sut = makeSUT() + sut.messagesCollectionView.messagesDataSource = messagesDataSource + messagesDataSource.messages.append(MockMessage( + image: UIImage(), + user: messagesDataSource.senders[0], + messageId: "test_id")) + + sut.messagesCollectionView.reloadData() + + let cell = sut.messagesCollectionView.dataSource?.collectionView( + sut.messagesCollectionView, + cellForItemAt: IndexPath(item: 0, section: 0)) + + XCTAssertNotNil(cell) + XCTAssertTrue(cell is MediaMessageCell) + } + + func testCellForItemWithVideoData_returnsMediaMessageCell() { + let messagesDataSource = MockMessagesDataSource() + let sut = makeSUT() + sut.messagesCollectionView.messagesDataSource = messagesDataSource + messagesDataSource.messages.append(MockMessage( + thumbnail: UIImage(), + user: messagesDataSource.senders[0], + messageId: "test_id")) + + sut.messagesCollectionView.reloadData() + + let cell = sut.messagesCollectionView.dataSource?.collectionView( + sut.messagesCollectionView, + cellForItemAt: IndexPath(item: 0, section: 0)) + + XCTAssertNotNil(cell) + XCTAssertTrue(cell is MediaMessageCell) + } + + func testCellForItemWithLocationData_returnsLocationMessageCell() { + let messagesDataSource = MockMessagesDataSource() + let sut = makeSUT() + sut.messagesCollectionView.messagesDataSource = messagesDataSource + messagesDataSource.messages.append(MockMessage( + location: CLLocation(latitude: 60.0, longitude: 70.0), + user: messagesDataSource.senders[0], + messageId: "test_id")) + + sut.messagesCollectionView.reloadData() + + let cell = sut.messagesCollectionView.dataSource?.collectionView( + sut.messagesCollectionView, + cellForItemAt: IndexPath(item: 0, section: 0)) + + XCTAssertNotNil(cell) + XCTAssertTrue(cell is LocationMessageCell) + } + + func testCellForItemWithAudioData_returnsAudioMessageCell() { + let messagesDataSource = MockMessagesDataSource() + let sut = makeSUT() + sut.messagesCollectionView.messagesDataSource = messagesDataSource + messagesDataSource.messages.append(MockMessage( + audioURL: URL(fileURLWithPath: ""), + duration: 4.0, + user: messagesDataSource.senders[0], + messageId: "test_id")) + + sut.messagesCollectionView.reloadData() + + let cell = sut.messagesCollectionView.dataSource?.collectionView( + sut.messagesCollectionView, + cellForItemAt: IndexPath(item: 0, section: 0)) + + XCTAssertNotNil(cell) + XCTAssertTrue(cell is AudioMessageCell) + } + + func testCellForItemWithLinkPreviewData_returnsLinkPreviewMessageCell() { + let messagesDataSource = MockMessagesDataSource() + let sut = makeSUT() + sut.messagesCollectionView.messagesDataSource = messagesDataSource + + let linkItem = MockLinkItem( + text: "https://link.test", + attributedText: nil, + url: URL(string: "https://github.com/MessageKit")!, + title: "Link Title", + teaser: "Link Teaser", + thumbnailImage: UIImage()) + + messagesDataSource.messages.append(MockMessage( + linkItem: linkItem, + user: messagesDataSource.senders[0], + messageId: "test_id")) + + sut.messagesCollectionView.reloadData() + + let cell = sut.messagesCollectionView.dataSource?.collectionView( + sut.messagesCollectionView, + cellForItemAt: IndexPath(item: 0, section: 0)) + + XCTAssertNotNil(cell) + XCTAssertTrue(cell is LinkPreviewMessageCell) + } + + // MARK: - Setups + + func testSubviewsSetup() { + let controller = MessagesViewController() + XCTAssertTrue(controller.view.subviews.contains(controller.messagesCollectionView)) + } + + func testDelegateAndDataSourceSetup() { + let controller = MessagesViewController() + controller.view.layoutIfNeeded() + XCTAssertTrue(controller.messagesCollectionView.delegate is MessagesViewController) + XCTAssertTrue(controller.messagesCollectionView.dataSource is MessagesViewController) + } + + func testDefaultPropertyValues() { + let controller = MessagesViewController() + XCTAssertNotNil(controller.messagesCollectionView) + XCTAssertTrue(controller.messagesCollectionView.collectionViewLayout is MessagesCollectionViewFlowLayout) + + controller.view.layoutIfNeeded() + XCTAssertTrue(controller.extendedLayoutIncludesOpaqueBars) + XCTAssertEqual(controller.view.backgroundColor, UIColor.collectionViewBackground) + XCTAssertEqual(controller.messagesCollectionView.keyboardDismissMode, UIScrollView.KeyboardDismissMode.interactive) + XCTAssertTrue(controller.messagesCollectionView.alwaysBounceVertical) + } + + // MARK: Private + + // swiftlint:disable:next weak_delegate + private var layoutDelegate = MockLayoutDelegate() + + // MARK: - Assistants + + private func makeMessages(for senders: [MockUser]) -> [MessageType] { + [ + MockMessage(text: "Text 1", user: senders[0], messageId: "test_id_1"), + MockMessage(text: "Text 2", user: senders[1], messageId: "test_id_2"), + ] + } } // MARK: - MockLayoutDelegate private class MockLayoutDelegate: MessagesLayoutDelegate, MessagesDisplayDelegate { - // MARK: - LocationMessageLayoutDelegate + // MARK: - LocationMessageLayoutDelegate - func heightForLocation(message _: MessageType, at _: IndexPath, with _: CGFloat, in _: MessagesCollectionView) -> CGFloat { - 0.0 - } + func heightForLocation(message _: MessageType, at _: IndexPath, with _: CGFloat, in _: MessagesCollectionView) -> CGFloat { + 0.0 + } - func heightForMedia(message _: MessageType, at _: IndexPath, with _: CGFloat, in _: MessagesCollectionView) -> CGFloat { - 10.0 - } + func heightForMedia(message _: MessageType, at _: IndexPath, with _: CGFloat, in _: MessagesCollectionView) -> CGFloat { + 10.0 + } - func snapshotOptionsForLocation( - message _: MessageType, - at _: IndexPath, - in _: MessagesCollectionView) + func snapshotOptionsForLocation( + message _: MessageType, + at _: IndexPath, + in _: MessagesCollectionView) -> LocationMessageSnapshotOptions - { - LocationMessageSnapshotOptions() - } + { + LocationMessageSnapshotOptions() + } } diff --git a/Tests/MessageKitTests/MessageKitTests.swift b/Tests/MessageKitTests/MessageKitTests.swift index 325faa977..1b5169b52 100644 --- a/Tests/MessageKitTests/MessageKitTests.swift +++ b/Tests/MessageKitTests/MessageKitTests.swift @@ -25,7 +25,7 @@ import XCTest @testable import MessageKit final class MessageKitTests: XCTestCase { - static var allTests = [ - "", - ] + static let allTests = [ + "", + ] } diff --git a/Tests/MessageKitTests/Mocks/MockMessagesDataSource.swift b/Tests/MessageKitTests/Mocks/MockMessagesDataSource.swift index d89240f64..dc9da0138 100644 --- a/Tests/MessageKitTests/Mocks/MockMessagesDataSource.swift +++ b/Tests/MessageKitTests/Mocks/MockMessagesDataSource.swift @@ -23,26 +23,27 @@ import Foundation @testable import MessageKit +@MainActor class MockMessagesDataSource: MessagesDataSource { - var messages: [MessageType] = [] - let senders: [MockUser] = [ - MockUser(senderId: "sender_1", displayName: "Sender 1"), - MockUser(senderId: "sender_2", displayName: "Sender 2"), - ] + var messages: [MessageType] = [] + let senders: [MockUser] = [ + MockUser(senderId: "sender_1", displayName: "Sender 1"), + MockUser(senderId: "sender_2", displayName: "Sender 2"), + ] - var currentUser: MockUser { - senders[0] - } + var currentUser: MockUser { + senders[0] + } - var currentSender: SenderType { - currentUser - } + var currentSender: SenderType { + currentUser + } - func numberOfSections(in _: MessagesCollectionView) -> Int { - messages.count - } + func numberOfSections(in _: MessagesCollectionView) -> Int { + messages.count + } - func messageForItem(at indexPath: IndexPath, in _: MessagesCollectionView) -> MessageType { - messages[indexPath.section] - } + func messageForItem(at indexPath: IndexPath, in _: MessagesCollectionView) -> MessageType { + messages[indexPath.section] + } } diff --git a/Tests/MessageKitTests/Protocols Tests/MessagesDisplayDelegateTests.swift b/Tests/MessageKitTests/Protocols Tests/MessagesDisplayDelegateTests.swift index 79fb809f7..2f51d61b9 100644 --- a/Tests/MessageKitTests/Protocols Tests/MessagesDisplayDelegateTests.swift +++ b/Tests/MessageKitTests/Protocols Tests/MessagesDisplayDelegateTests.swift @@ -26,224 +26,219 @@ import XCTest // MARK: - MessagesDisplayDelegateTests +@MainActor final class MessagesDisplayDelegateTests: XCTestCase { - // MARK: Internal - - override func setUp() { - super.setUp() - - sut = MockMessagesViewController() - _ = sut.view - sut.beginAppearanceTransition(true, animated: true) - sut.endAppearanceTransition() - sut.viewDidLoad() - sut.view.layoutIfNeeded() - } - - override func tearDown() { - sut = nil - - super.tearDown() - } - - func testBackGroundColorDefaultState() { - XCTAssertEqual( - sut.backgroundColor( - for: sut.dataProvider.messages[0], - at: IndexPath(item: 0, section: 0), - in: sut.messagesCollectionView), - UIColor.outgoingMessageBackground) - XCTAssertNotEqual( - sut.backgroundColor( - for: sut.dataProvider.messages[0], - at: IndexPath(item: 0, section: 0), - in: sut.messagesCollectionView), - UIColor.incomingMessageBackground) - XCTAssertEqual( - sut.backgroundColor( - for: sut.dataProvider.messages[1], - at: IndexPath(item: 1, section: 0), - in: sut.messagesCollectionView), - UIColor.incomingMessageBackground) - XCTAssertNotEqual( - sut.backgroundColor( - for: sut.dataProvider.messages[1], - at: IndexPath(item: 1, section: 0), - in: sut.messagesCollectionView), - UIColor.outgoingMessageBackground) - } - - func testBackgroundColorWithoutDataSource_returnsWhiteForDefault() { - sut.messagesCollectionView.messagesDataSource = nil - let backgroundColor = sut.backgroundColor( - for: sut.dataProvider.messages[0], - at: IndexPath(item: 0, section: 0), - in: sut.messagesCollectionView) - - XCTAssertEqual(backgroundColor, UIColor.white) - } - - func testBackgroundColorForMessageWithEmoji_returnsClearForDefault() { - sut.dataProvider.messages.append(MockMessage( - emoji: "🤔", - user: sut.dataProvider.currentUser, - messageId: "003")) - let backgroundColor = sut.backgroundColor( - for: sut.dataProvider.messages[2], - at: IndexPath(item: 0, section: 0), - in: sut.messagesCollectionView) - - XCTAssertEqual(backgroundColor, .clear) - } - - func testCellTopLabelDefaultState() { - XCTAssertNil(sut.dataProvider.cellTopLabelAttributedText( - for: sut.dataProvider.messages[0], - at: IndexPath(item: 0, section: 0))) - } - - func testMessageBottomLabelDefaultState() { - XCTAssertNil(sut.dataProvider.messageBottomLabelAttributedText( - for: sut.dataProvider.messages[0], - at: IndexPath(item: 0, section: 0))) - } - - func testMessageStyle_returnsBubleTypeForDefault() { - let type = sut.messageStyle( - for: sut.dataProvider.messages[0], - at: IndexPath(item: 0, section: 0), - in: sut.messagesCollectionView) - - let result: Bool - switch type { - case .bubble: - result = true - default: - result = false - } - - XCTAssertTrue(result) - } - - func testMessageHeaderView_isNotNil() { - let indexPath = IndexPath(item: 0, section: 1) - XCTAssert(sut.dataProvider != nil) - let headerView = sut.messageHeaderView(for: indexPath, in: sut.messagesCollectionView) - XCTAssertNotNil(headerView) - } - - // MARK: Private - - private var sut: MockMessagesViewController! -} + // MARK: - Private helper API -// MARK: - TextMessageDisplayDelegateTests + private func makeSUT() -> MockMessagesViewController { + let sut = MockMessagesViewController() + _ = sut.view + sut.beginAppearanceTransition(true, animated: true) + sut.endAppearanceTransition() + sut.viewDidLoad() + sut.view.layoutIfNeeded() -class TextMessageDisplayDelegateTests: XCTestCase { - // MARK: Internal + return sut + } - override func setUp() { - super.setUp() + // MARK: Internal + + func testBackGroundColorDefaultState() { + let sut = makeSUT() + XCTAssertEqual( + sut.backgroundColor( + for: sut.dataProvider.messages[0], + at: IndexPath(item: 0, section: 0), + in: sut.messagesCollectionView), + UIColor.outgoingMessageBackground) + XCTAssertNotEqual( + sut.backgroundColor( + for: sut.dataProvider.messages[0], + at: IndexPath(item: 0, section: 0), + in: sut.messagesCollectionView), + UIColor.incomingMessageBackground) + XCTAssertEqual( + sut.backgroundColor( + for: sut.dataProvider.messages[1], + at: IndexPath(item: 1, section: 0), + in: sut.messagesCollectionView), + UIColor.incomingMessageBackground) + XCTAssertNotEqual( + sut.backgroundColor( + for: sut.dataProvider.messages[1], + at: IndexPath(item: 1, section: 0), + in: sut.messagesCollectionView), + UIColor.outgoingMessageBackground) + } + + func testBackgroundColorWithoutDataSource_returnsWhiteForDefault() { + let sut = makeSUT() + sut.messagesCollectionView.messagesDataSource = nil + let backgroundColor = sut.backgroundColor( + for: sut.dataProvider.messages[0], + at: IndexPath(item: 0, section: 0), + in: sut.messagesCollectionView) - sut = MockMessagesViewController() - _ = sut.view - sut.beginAppearanceTransition(true, animated: true) - sut.endAppearanceTransition() - } + XCTAssertEqual(backgroundColor, UIColor.white) + } - override func tearDown() { - sut = nil + func testBackgroundColorForMessageWithEmoji_returnsClearForDefault() { + let sut = makeSUT() + sut.dataProvider.messages.append(MockMessage( + emoji: "🤔", + user: sut.dataProvider.currentUser, + messageId: "003")) + let backgroundColor = sut.backgroundColor( + for: sut.dataProvider.messages[2], + at: IndexPath(item: 0, section: 0), + in: sut.messagesCollectionView) + + XCTAssertEqual(backgroundColor, .clear) + } - super.tearDown() - } + func testCellTopLabelDefaultState() { + let sut = makeSUT() + XCTAssertNil(sut.dataProvider.cellTopLabelAttributedText( + for: sut.dataProvider.messages[0], + at: IndexPath(item: 0, section: 0))) + } + + func testMessageBottomLabelDefaultState() { + let sut = makeSUT() + XCTAssertNil(sut.dataProvider.messageBottomLabelAttributedText( + for: sut.dataProvider.messages[0], + at: IndexPath(item: 0, section: 0))) + } + + func testMessageStyle_returnsBubleTypeForDefault() { + let sut = makeSUT() + let type = sut.messageStyle( + for: sut.dataProvider.messages[0], + at: IndexPath(item: 0, section: 0), + in: sut.messagesCollectionView) + + let result: Bool + switch type { + case .bubble: + result = true + default: + result = false + } + + XCTAssertTrue(result) + } + + func testMessageHeaderView_isNotNil() { + let sut = makeSUT() + let indexPath = IndexPath(item: 0, section: 1) + XCTAssert(sut.dataProvider != nil) + let headerView = sut.messageHeaderView(for: indexPath, in: sut.messagesCollectionView) + XCTAssertNotNil(headerView) + } +} - func testTextColorFromCurrentSender_returnsWhiteForDefault() { - let textColor = sut.textColor( - for: sut.dataProvider.messages[0], - at: IndexPath(item: 0, section: 0), - in: sut.messagesCollectionView) +// MARK: - TextMessageDisplayDelegateTests - XCTAssertEqual(textColor, UIColor.outgoingMessageLabel) - } +@MainActor +class TextMessageDisplayDelegateTests: XCTestCase { + // MARK: - Private helper API + + private func makeSUT() -> MockMessagesViewController { + let sut = MockMessagesViewController() + _ = sut.view + sut.beginAppearanceTransition(true, animated: true) + sut.endAppearanceTransition() + return sut + } - func testTextColorFromYou_returnsDarkTextForDefault() { - let textColor = sut.textColor( - for: sut.dataProvider.messages[1], - at: IndexPath(item: 0, section: 0), - in: sut.messagesCollectionView) + func testTextColorFromCurrentSender_returnsWhiteForDefault() { + let sut = makeSUT() + let textColor = sut.textColor( + for: sut.dataProvider.messages[0], + at: IndexPath(item: 0, section: 0), + in: sut.messagesCollectionView) - XCTAssertEqual(textColor, UIColor.incomingMessageLabel) - } + XCTAssertEqual(textColor, UIColor.outgoingMessageLabel) + } - func testTextColorWithoutDataSource_returnsDarkTextForDefault() { - let dataSource = sut.makeDataSource() - sut.messagesCollectionView.messagesDataSource = dataSource - let textColor = sut.textColor( - for: sut.dataProvider.messages[1], - at: IndexPath(item: 0, section: 0), - in: sut.messagesCollectionView) + func testTextColorFromYou_returnsDarkTextForDefault() { + let sut = makeSUT() + let textColor = sut.textColor( + for: sut.dataProvider.messages[1], + at: IndexPath(item: 0, section: 0), + in: sut.messagesCollectionView) - XCTAssertEqual(textColor, UIColor.incomingMessageLabel) - } + XCTAssertEqual(textColor, UIColor.incomingMessageLabel) + } - func testEnableDetectors_returnsEmptyForDefault() { - let detectors = sut.enabledDetectors( - for: sut.dataProvider.messages[1], - at: IndexPath(item: 0, section: 0), - in: sut.messagesCollectionView) - let expectedDetectors: [DetectorType] = [] + func testTextColorWithoutDataSource_returnsDarkTextForDefault() { + let sut = makeSUT() + let dataSource = sut.makeDataSource() + sut.messagesCollectionView.messagesDataSource = dataSource + let textColor = sut.textColor( + for: sut.dataProvider.messages[1], + at: IndexPath(item: 0, section: 0), + in: sut.messagesCollectionView) - XCTAssertEqual(detectors, expectedDetectors) - } + XCTAssertEqual(textColor, UIColor.incomingMessageLabel) + } - // MARK: Private + func testEnableDetectors_returnsEmptyForDefault() { + let sut = makeSUT() + let detectors = sut.enabledDetectors( + for: sut.dataProvider.messages[1], + at: IndexPath(item: 0, section: 0), + in: sut.messagesCollectionView) + let expectedDetectors: [DetectorType] = [] - private var sut: MockMessagesViewController! + XCTAssertEqual(detectors, expectedDetectors) + } } // MARK: - MockMessagesViewController +@MainActor private class MockMessagesViewController: MessagesViewController, MessagesDisplayDelegate, MessagesLayoutDelegate { - // MARK: Internal + // MARK: Internal - var dataProvider: MockMessagesDataSource! + var dataProvider: MockMessagesDataSource! - func heightForLocation(message _: MessageType, at _: IndexPath, with _: CGFloat, in _: MessagesCollectionView) -> CGFloat { - 200 - } + func heightForLocation(message _: MessageType, at _: IndexPath, with _: CGFloat, in _: MessagesCollectionView) -> CGFloat { + 200 + } - override func viewDidLoad() { - super.viewDidLoad() + override func viewDidLoad() { + super.viewDidLoad() - dataProvider = makeDataSource() - messagesCollectionView.messagesDisplayDelegate = self - messagesCollectionView.messagesDataSource = dataProvider - messagesCollectionView.messagesLayoutDelegate = self - messagesCollectionView.reloadData() - } + dataProvider = makeDataSource() + messagesCollectionView.messagesDisplayDelegate = self + messagesCollectionView.messagesDataSource = dataProvider + messagesCollectionView.messagesLayoutDelegate = self + messagesCollectionView.reloadData() + } - func snapshotOptionsForLocation( - message _: MessageType, - at _: IndexPath, - in _: MessagesCollectionView) + func snapshotOptionsForLocation( + message _: MessageType, + at _: IndexPath, + in _: MessagesCollectionView) -> LocationMessageSnapshotOptions - { - LocationMessageSnapshotOptions() - } - - // MARK: Fileprivate - - fileprivate func makeDataSource() -> MockMessagesDataSource { - let dataSource = MockMessagesDataSource() - dataSource.messages.append(MockMessage( - text: "Text 1", - user: dataSource.senders[0], - messageId: "001")) - dataSource.messages.append(MockMessage( - text: "Text 2", - user: dataSource.senders[1], - messageId: "002")) - - return dataSource - } + { + LocationMessageSnapshotOptions() + } + + // MARK: Fileprivate + + fileprivate func makeDataSource() -> MockMessagesDataSource { + let dataSource = MockMessagesDataSource() + dataSource.messages.append(MockMessage( + text: "Text 1", + user: dataSource.senders[0], + messageId: "001")) + dataSource.messages.append(MockMessage( + text: "Text 2", + user: dataSource.senders[1], + messageId: "002")) + + return dataSource + } } diff --git a/Tests/MessageKitTests/Views Tests/AvatarViewTests.swift b/Tests/MessageKitTests/Views Tests/AvatarViewTests.swift index ad3f99dd4..846b1c01e 100644 --- a/Tests/MessageKitTests/Views Tests/AvatarViewTests.swift +++ b/Tests/MessageKitTests/Views Tests/AvatarViewTests.swift @@ -24,62 +24,69 @@ import Foundation import XCTest @testable import MessageKit +@MainActor final class AvatarViewTests: XCTestCase { - var avatarView: AvatarView! + // MARK: - Private helper API - override func setUp() { - super.setUp() - avatarView = AvatarView() - avatarView.frame.size = CGSize(width: 30, height: 30) - } + private func makeAvatarView() -> AvatarView { + let avatarView = AvatarView() + avatarView.frame.size = CGSize(width: 30, height: 30) + return avatarView + } - override func tearDown() { - super.tearDown() - avatarView = nil - } + func testNoParams() { + let avatarView = makeAvatarView() - func testNoParams() { - XCTAssertEqual(avatarView.layer.cornerRadius, 15.0) - // For certain dynamic colors, need to compare cgColor in XCTest - // https://stackoverflow.com/questions/58065340/how-to-compare-two-uidynamicprovidercolor - XCTAssertEqual(avatarView.backgroundColor!.cgColor, UIColor.avatarViewBackground.cgColor) - } + XCTAssertEqual(avatarView.layer.cornerRadius, 15.0) + // For certain dynamic colors, need to compare cgColor in XCTest + // https://stackoverflow.com/questions/58065340/how-to-compare-two-uidynamicprovidercolor + XCTAssertEqual(avatarView.backgroundColor!.cgColor, UIColor.avatarViewBackground.cgColor) + } - func testWithImage() { - let avatar = Avatar(image: UIImage()) - avatarView.set(avatar: avatar) - XCTAssertEqual(avatar.initials, "?") - XCTAssertEqual(avatarView.layer.cornerRadius, 15.0) - XCTAssertEqual(avatarView.backgroundColor!.cgColor, UIColor.avatarViewBackground.cgColor) - } + func testWithImage() { + let avatarView = makeAvatarView() - func testInitialsOnly() { - let avatar = Avatar(initials: "DL") - avatarView.set(avatar: avatar) - XCTAssertEqual(avatarView.initials, avatar.initials) - XCTAssertEqual(avatar.initials, "DL") - XCTAssertEqual(avatarView.layer.cornerRadius, 15.0) - XCTAssertEqual(avatarView.backgroundColor!.cgColor, UIColor.avatarViewBackground.cgColor) - } + let avatar = Avatar(image: UIImage()) + avatarView.set(avatar: avatar) + XCTAssertEqual(avatar.initials, "?") + XCTAssertEqual(avatarView.layer.cornerRadius, 15.0) + XCTAssertEqual(avatarView.backgroundColor!.cgColor, UIColor.avatarViewBackground.cgColor) + } - func testSetBackground() { - XCTAssertEqual(avatarView.backgroundColor!.cgColor, UIColor.avatarViewBackground.cgColor) - avatarView.backgroundColor = UIColor.red - XCTAssertEqual(avatarView.backgroundColor!, UIColor.red) - } + func testInitialsOnly() { + let avatarView = makeAvatarView() - func testGetImage() { - let image = UIImage() - let avatar = Avatar(image: image) - avatarView.set(avatar: avatar) - XCTAssertEqual(avatarView.image, image) - } + let avatar = Avatar(initials: "DL") + avatarView.set(avatar: avatar) + XCTAssertEqual(avatarView.initials, avatar.initials) + XCTAssertEqual(avatar.initials, "DL") + XCTAssertEqual(avatarView.layer.cornerRadius, 15.0) + XCTAssertEqual(avatarView.backgroundColor!.cgColor, UIColor.avatarViewBackground.cgColor) + } - func testRoundedCorners() { - let avatar = Avatar(image: UIImage()) - avatarView.set(avatar: avatar) - XCTAssertEqual(avatarView.layer.cornerRadius, 15.0) - avatarView.setCorner(radius: 2) - XCTAssertEqual(avatarView.layer.cornerRadius, 2.0) - } + func testSetBackground() { + let avatarView = makeAvatarView() + XCTAssertEqual(avatarView.backgroundColor!.cgColor, UIColor.avatarViewBackground.cgColor) + avatarView.backgroundColor = UIColor.red + XCTAssertEqual(avatarView.backgroundColor!, UIColor.red) + } + + func testGetImage() { + let avatarView = makeAvatarView() + + let image = UIImage() + let avatar = Avatar(image: image) + avatarView.set(avatar: avatar) + XCTAssertEqual(avatarView.image, image) + } + + func testRoundedCorners() { + let avatarView = makeAvatarView() + + let avatar = Avatar(image: UIImage()) + avatarView.set(avatar: avatar) + XCTAssertEqual(avatarView.layer.cornerRadius, 15.0) + avatarView.setCorner(radius: 2) + XCTAssertEqual(avatarView.layer.cornerRadius, 2.0) + } } diff --git a/Tests/MessageKitTests/Views Tests/MessageCollectionViewCellTests.swift b/Tests/MessageKitTests/Views Tests/MessageCollectionViewCellTests.swift index 382f5d9d6..62190e3ea 100644 --- a/Tests/MessageKitTests/Views Tests/MessageCollectionViewCellTests.swift +++ b/Tests/MessageKitTests/Views Tests/MessageCollectionViewCellTests.swift @@ -26,62 +26,56 @@ import XCTest // MARK: - MessageContentCellTests +@MainActor final class MessageContentCellTests: XCTestCase { - var cell: MessageContentCell! - let frame = CGRect(x: 0, y: 0, width: 100, height: 100) + let frame = CGRect(x: 0, y: 0, width: 100, height: 100) - override func setUp() { - super.setUp() - cell = MessageContentCell(frame: frame) - } - - override func tearDown() { - cell = nil - super.tearDown() - } - - func testInit() { - XCTAssertEqual(cell.contentView.autoresizingMask, [.flexibleWidth, .flexibleHeight]) - XCTAssert(cell.contentView.subviews.contains(cell.cellTopLabel)) - XCTAssert(cell.contentView.subviews.contains(cell.messageBottomLabel)) - XCTAssert(cell.contentView.subviews.contains(cell.avatarView)) - XCTAssert(cell.contentView.subviews.contains(cell.messageContainerView)) - } + func testInit() { + let cell = MessageContentCell(frame: frame) + XCTAssertEqual(cell.contentView.autoresizingMask, [.flexibleWidth, .flexibleHeight]) + XCTAssert(cell.contentView.subviews.contains(cell.cellTopLabel)) + XCTAssert(cell.contentView.subviews.contains(cell.messageBottomLabel)) + XCTAssert(cell.contentView.subviews.contains(cell.avatarView)) + XCTAssert(cell.contentView.subviews.contains(cell.messageContainerView)) + } - func testMessageContainerViewPropertiesSetup() { - XCTAssertTrue(cell.messageContainerView.clipsToBounds) - XCTAssertTrue(cell.messageContainerView.layer.masksToBounds) - } + func testMessageContainerViewPropertiesSetup() { + let cell = MessageContentCell(frame: frame) + XCTAssertTrue(cell.messageContainerView.clipsToBounds) + XCTAssertTrue(cell.messageContainerView.layer.masksToBounds) + } - func testPrepareForReuse() { - cell.prepareForReuse() - XCTAssertNil(cell.cellTopLabel.text) - XCTAssertNil(cell.cellTopLabel.attributedText) - XCTAssertNil(cell.messageBottomLabel.text) - XCTAssertNil(cell.messageBottomLabel.attributedText) - } + func testPrepareForReuse() { + let cell = MessageContentCell(frame: frame) + cell.prepareForReuse() + XCTAssertNil(cell.cellTopLabel.text) + XCTAssertNil(cell.cellTopLabel.attributedText) + XCTAssertNil(cell.messageBottomLabel.text) + XCTAssertNil(cell.messageBottomLabel.attributedText) + } - func testApplyLayoutAttributes() { - let layoutAttributes = MessagesCollectionViewLayoutAttributes() - layoutAttributes.avatarPosition = AvatarPosition(horizontal: .cellLeading, vertical: .cellBottom) - cell.apply(layoutAttributes) + func testApplyLayoutAttributes() { + let cell = MessageContentCell(frame: frame) + let layoutAttributes = MessagesCollectionViewLayoutAttributes() + layoutAttributes.avatarPosition = AvatarPosition(horizontal: .cellLeading, vertical: .cellBottom) + cell.apply(layoutAttributes) - XCTAssertEqual(cell.avatarView.frame, layoutAttributes.frame) - XCTAssertEqual(cell.messageContainerView.frame.size, layoutAttributes.messageContainerSize) - XCTAssertEqual(cell.cellTopLabel.frame.size, layoutAttributes.cellTopLabelSize) - XCTAssertEqual(cell.messageBottomLabel.frame.size, layoutAttributes.messageBottomLabelSize) - } + XCTAssertEqual(cell.avatarView.frame, layoutAttributes.frame) + XCTAssertEqual(cell.messageContainerView.frame.size, layoutAttributes.messageContainerSize) + XCTAssertEqual(cell.cellTopLabel.frame.size, layoutAttributes.cellTopLabelSize) + XCTAssertEqual(cell.messageBottomLabel.frame.size, layoutAttributes.messageBottomLabelSize) + } } extension MessageContentCellTests { - private class MockMessagesDisplayDelegate: MessagesDisplayDelegate { - func snapshotOptionsForLocation( - message _: MessageType, - at _: IndexPath, - in _: MessagesCollectionView) - -> LocationMessageSnapshotOptions - { - LocationMessageSnapshotOptions() + private class MockMessagesDisplayDelegate: MessagesDisplayDelegate { + func snapshotOptionsForLocation( + message _: MessageType, + at _: IndexPath, + in _: MessagesCollectionView) + -> LocationMessageSnapshotOptions + { + LocationMessageSnapshotOptions() + } } - } } diff --git a/Tests/MessageKitTests/Views Tests/MessagesCollectionViewTests.swift b/Tests/MessageKitTests/Views Tests/MessagesCollectionViewTests.swift index 0da2a55f3..84e0ca902 100644 --- a/Tests/MessageKitTests/Views Tests/MessagesCollectionViewTests.swift +++ b/Tests/MessageKitTests/Views Tests/MessagesCollectionViewTests.swift @@ -23,24 +23,15 @@ import XCTest @testable import MessageKit +@MainActor class MessagesCollectionViewTests: XCTestCase { - var messagesCollectionView: MessagesCollectionView! - let rect = CGRect(x: 0, y: 0, width: 100, height: 100) - let layout = MessagesCollectionViewFlowLayout() + let rect = CGRect(x: 0, y: 0, width: 100, height: 100) + let layout = MessagesCollectionViewFlowLayout() - override func setUp() { - super.setUp() - messagesCollectionView = MessagesCollectionView(frame: rect, collectionViewLayout: layout) - } - - override func tearDown() { - messagesCollectionView = nil - super.tearDown() - } - - func testInit() { - XCTAssertEqual(messagesCollectionView.frame, rect) - XCTAssertEqual(messagesCollectionView.collectionViewLayout, layout) - XCTAssertEqual(messagesCollectionView.backgroundColor, UIColor.collectionViewBackground) - } + func testInit() { + let messagesCollectionView = MessagesCollectionView(frame: rect, collectionViewLayout: layout) + XCTAssertEqual(messagesCollectionView.frame, rect) + XCTAssertEqual(messagesCollectionView.collectionViewLayout, layout) + XCTAssertEqual(messagesCollectionView.backgroundColor, UIColor.collectionViewBackground) + } } From ba4f2b9afa40f17c019f254441375e3150ea93b2 Mon Sep 17 00:00:00 2001 From: Jakub Kaspar Date: Mon, 25 Nov 2024 17:44:21 +0100 Subject: [PATCH 09/12] Improve error message --- Sources/Views/Cells/LocationMessageCell.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Views/Cells/LocationMessageCell.swift b/Sources/Views/Cells/LocationMessageCell.swift index 3f42c7171..0fc1b1fe7 100644 --- a/Sources/Views/Cells/LocationMessageCell.swift +++ b/Sources/Views/Cells/LocationMessageCell.swift @@ -71,7 +71,7 @@ open class LocationMessageCell: MessageContentCell { at: indexPath, in: messagesCollectionView) - guard case .location(let locationItem) = message.kind else { fatalError("Configuring LocationMessageCell with wrong") } + guard case .location(let locationItem) = message.kind else { fatalError("Configuring LocationMessageCell with wrong message kind") } activityIndicator.startAnimating() From 3a0f20b96a0a91ac51fb9b1249636816a4da08db Mon Sep 17 00:00:00 2001 From: Jakub Kaspar Date: Mon, 25 Nov 2024 17:48:38 +0100 Subject: [PATCH 10/12] Return empty cell in case location is invalid --- Sources/Views/Cells/LocationMessageCell.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Sources/Views/Cells/LocationMessageCell.swift b/Sources/Views/Cells/LocationMessageCell.swift index 0fc1b1fe7..44601b1b8 100644 --- a/Sources/Views/Cells/LocationMessageCell.swift +++ b/Sources/Views/Cells/LocationMessageCell.swift @@ -58,6 +58,12 @@ open class LocationMessageCell: MessageContentCell { and messagesCollectionView: MessagesCollectionView) { super.configure(with: message, at: indexPath, and: messagesCollectionView) + + guard case .location(let locationItem) = message.kind else { fatalError("Configuring LocationMessageCell with wrong message kind") } + guard CLLocationCoordinate2DIsValid(locationItem.location.coordinate) else { + return + } + guard let displayDelegate = messagesCollectionView.messagesDisplayDelegate else { fatalError(MessageKitError.nilMessagesDisplayDelegate) } @@ -71,8 +77,6 @@ open class LocationMessageCell: MessageContentCell { at: indexPath, in: messagesCollectionView) - guard case .location(let locationItem) = message.kind else { fatalError("Configuring LocationMessageCell with wrong message kind") } - activityIndicator.startAnimating() let snapshotOptions = MKMapSnapshotter.Options() From e98ddbf1120e7b45721e61cf29352839bd2e4e36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ka=C5=A1par?= Date: Fri, 6 Dec 2024 12:44:33 +0100 Subject: [PATCH 11/12] Update README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d34412a50..95b077216 100644 --- a/README.md +++ b/README.md @@ -48,8 +48,10 @@ Older versions of Swift and Xcode don't support MessageKit via SPM. ## Requirements -- **iOS 13** or later -- **Swift 5.5** or later +- **iOS 14** or later +- **Swift 6** or later +- +> For iOS 13 or Swift 5.x please use version 4.3.0 > For iOS 12 or CocoaPods please use version 3.8.0 From dc48356f830d40de5acc0e126a44257d0a1329b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ka=C5=A1par?= Date: Fri, 6 Dec 2024 12:45:52 +0100 Subject: [PATCH 12/12] Update deploy_docs.yml --- .github/workflows/deploy_docs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy_docs.yml b/.github/workflows/deploy_docs.yml index 9648963dd..a05b6143f 100644 --- a/.github/workflows/deploy_docs.yml +++ b/.github/workflows/deploy_docs.yml @@ -15,7 +15,7 @@ concurrency: jobs: build_docs: - runs-on: macos-14 + runs-on: macos-15 steps: - name: Checkout 🛎️ uses: actions/checkout@v4 @@ -33,7 +33,7 @@ jobs: --hosting-base-path MessageKit/InputBarAccessoryView \ --output-path docs/InputBarAccessoryView - name: Deploy to GitHub Pages - uses: peaceiris/actions-gh-pages@v3 + uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./docs