From f5f09f22d47b3ced4a7b0459dcb925834377a22c Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Wed, 7 Aug 2019 13:27:53 -0700 Subject: [PATCH] Adds support for copying and opening deep link URLs --- Delta.xcodeproj/project.pbxproj | 4 ++ Delta/AppDelegate.swift | 4 ++ Delta/Deep Linking/CopyDeepLinkActivity.swift | 53 ++++++++++++++++++ Delta/Deep Linking/DeepLink.swift | 10 +--- .../GameCollectionViewController.swift | 4 +- Delta/Supporting Files/Info.plist | 10 ++++ .../Link.imageset/Contents.json | 26 +++++++++ .../Assets.xcassets/Link.imageset/Link@1x.png | Bin 0 -> 1811 bytes .../Assets.xcassets/Link.imageset/Link@2x.png | Bin 0 -> 3673 bytes .../Assets.xcassets/Link.imageset/Link@3x.png | Bin 0 -> 5898 bytes 10 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 Delta/Deep Linking/CopyDeepLinkActivity.swift create mode 100644 Resources/Assets.xcassets/Link.imageset/Contents.json create mode 100644 Resources/Assets.xcassets/Link.imageset/Link@1x.png create mode 100644 Resources/Assets.xcassets/Link.imageset/Link@2x.png create mode 100644 Resources/Assets.xcassets/Link.imageset/Link@3x.png diff --git a/Delta.xcodeproj/project.pbxproj b/Delta.xcodeproj/project.pbxproj index eddccca..07a2a0e 100644 --- a/Delta.xcodeproj/project.pbxproj +++ b/Delta.xcodeproj/project.pbxproj @@ -134,6 +134,7 @@ BFB359452278FD8100CFD920 /* N64DeltaCore_RSP.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BFB359422278FD6800CFD920 /* N64DeltaCore_RSP.framework */; }; BFB359462278FD8100CFD920 /* N64DeltaCore_RSP.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BFB359422278FD6800CFD920 /* N64DeltaCore_RSP.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; BFBAB2E31EB685A2004E0B0E /* DeltaCoreProtocol+Delta.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFBAB2E21EB685A2004E0B0E /* DeltaCoreProtocol+Delta.swift */; }; + BFC1F2CC22F9515F00606A45 /* CopyDeepLinkActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC1F2CB22F9515F00606A45 /* CopyDeepLinkActivity.swift */; }; BFC3628021ADE2BA00EF2BE6 /* UIAlertController+Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC3627F21ADE2BA00EF2BE6 /* UIAlertController+Error.swift */; }; BFC6F7B81F435BC500221B96 /* Input+Display.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC6F7B71F435BC500221B96 /* Input+Display.swift */; }; BFC9B7391CEFCD34008629BB /* CheatsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC9B7381CEFCD34008629BB /* CheatsViewController.swift */; }; @@ -314,6 +315,7 @@ BFB359422278FD6800CFD920 /* N64DeltaCore_RSP.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = N64DeltaCore_RSP.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BFBAB2E21EB685A2004E0B0E /* DeltaCoreProtocol+Delta.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DeltaCoreProtocol+Delta.swift"; sourceTree = ""; }; BFC134E01AAD82460087AD7B /* SNESDeltaCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SNESDeltaCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BFC1F2CB22F9515F00606A45 /* CopyDeepLinkActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyDeepLinkActivity.swift; sourceTree = ""; }; BFC3627F21ADE2BA00EF2BE6 /* UIAlertController+Error.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIAlertController+Error.swift"; sourceTree = ""; }; BFC6F7B71F435BC500221B96 /* Input+Display.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Input+Display.swift"; sourceTree = ""; }; BFC9B7381CEFCD34008629BB /* CheatsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheatsViewController.swift; sourceTree = ""; }; @@ -462,6 +464,7 @@ children = ( BF525EE91FF6CD12004AA849 /* DeepLink.swift */, BF525EE71FF5F370004AA849 /* DeepLinkController.swift */, + BFC1F2CB22F9515F00606A45 /* CopyDeepLinkActivity.swift */, ); path = "Deep Linking"; sourceTree = ""; @@ -1035,6 +1038,7 @@ BFAA1FED1B8AA4FA00495943 /* Settings.swift in Sources */, BFA0D1271D3AE1F600565894 /* GameViewController.swift in Sources */, BF59428A1E09BC8B0051894B /* _SaveState.swift in Sources */, + BFC1F2CC22F9515F00606A45 /* CopyDeepLinkActivity.swift in Sources */, BF5942801E09BC830051894B /* SaveState.swift in Sources */, BF59428E1E09BCFB0051894B /* ImportController.swift in Sources */, BF13A7581D5D2FD9000BB055 /* EmulatorCore+Cheats.swift in Sources */, diff --git a/Delta/AppDelegate.swift b/Delta/AppDelegate.swift index 564841a..e53bd0a 100644 --- a/Delta/AppDelegate.swift +++ b/Delta/AppDelegate.swift @@ -111,6 +111,10 @@ extension AppDelegate { return DropboxService.shared.handleDropboxURL(url) } + else if url.scheme?.lowercased() == "delta" + { + return self.deepLinkController.handle(.url(url)) + } return false } diff --git a/Delta/Deep Linking/CopyDeepLinkActivity.swift b/Delta/Deep Linking/CopyDeepLinkActivity.swift new file mode 100644 index 0000000..3dfc956 --- /dev/null +++ b/Delta/Deep Linking/CopyDeepLinkActivity.swift @@ -0,0 +1,53 @@ +// +// CopyDeepLinkActivity.swift +// Delta +// +// Created by Riley Testut on 8/5/19. +// Copyright © 2019 Riley Testut. All rights reserved. +// + +import UIKit + +extension UIActivity.ActivityType +{ + static let copyDeepLink = UIActivity.ActivityType("com.rileytestut.Delta.CopyDeepLink") +} + +class CopyDeepLinkActivity: UIActivity +{ + override class var activityCategory: UIActivity.Category { + return .action + } + + override var activityType: UIActivity.ActivityType? { + return .copyDeepLink + } + + override var activityTitle: String? { + return NSLocalizedString("Copy Deep Link", comment: "") + } + + override var activityImage: UIImage? { + return UIImage(named: "Link") + } + + override func canPerform(withActivityItems activityItems: [Any]) -> Bool + { + if activityItems.contains(where: { $0 is Game }) + { + return true + } + else + { + return false + } + } + + override func prepare(withActivityItems activityItems: [Any]) + { + guard let game = activityItems.first(where: { $0 is Game }) as? Game else { return } + + let deepLink = URL(action: .launchGame(identifier: game.identifier)) + UIPasteboard.general.url = deepLink + } +} diff --git a/Delta/Deep Linking/DeepLink.swift b/Delta/Deep Linking/DeepLink.swift index cb617e3..2395d9a 100644 --- a/Delta/Deep Linking/DeepLink.swift +++ b/Delta/Deep Linking/DeepLink.swift @@ -12,16 +12,12 @@ extension URL { init(action: DeepLink.Action) { - var components = URLComponents() - components.host = action.type.rawValue - switch action { - case .launchGame(let identifier): components.path = identifier + case .launchGame(let identifier): + let deepLinkURL = URL(string: "delta://\(action.type.rawValue)/\(identifier)")! + self = deepLinkURL } - - let url = components.url! - self = url } } diff --git a/Delta/Game Selection/GameCollectionViewController.swift b/Delta/Game Selection/GameCollectionViewController.swift index 6586b19..1eba284 100644 --- a/Delta/Game Selection/GameCollectionViewController.swift +++ b/Delta/Game Selection/GameCollectionViewController.swift @@ -585,7 +585,9 @@ private extension GameCollectionViewController print(error) } - let activityViewController = UIActivityViewController(activityItems: [symbolicURL], applicationActivities: nil) + let copyDeepLinkActivity = CopyDeepLinkActivity() + + let activityViewController = UIActivityViewController(activityItems: [symbolicURL, game], applicationActivities: [copyDeepLinkActivity]) activityViewController.completionWithItemsHandler = { (activityType, finished, returnedItems, error) in do { diff --git a/Delta/Supporting Files/Info.plist b/Delta/Supporting Files/Info.plist index 8ffff72..f2e1b65 100644 --- a/Delta/Supporting Files/Info.plist +++ b/Delta/Supporting Files/Info.plist @@ -149,6 +149,16 @@ db-f5btgysf9ma9bb6 + + CFBundleTypeRole + Editor + CFBundleURLName + Delta + CFBundleURLSchemes + + delta + + CFBundleVersion 10 diff --git a/Resources/Assets.xcassets/Link.imageset/Contents.json b/Resources/Assets.xcassets/Link.imageset/Contents.json new file mode 100644 index 0000000..0668ba2 --- /dev/null +++ b/Resources/Assets.xcassets/Link.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Link@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Link@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Link@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/Resources/Assets.xcassets/Link.imageset/Link@1x.png b/Resources/Assets.xcassets/Link.imageset/Link@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..524f73cfdfe3f99de803af87b0ba5c9bb6e969ac GIT binary patch literal 1811 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbBSkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#My5g&JNkFq9fFFuY1&V6Xz}5igjtE6@fg!CBxDSzC zHb_`sNdc^+B->Ug!Z$#{Ilm}X!Bo#g&p^qJOF==wrYI%ND#*nRsvXF)RmvzSDX`Ml zFE20GD>v55FG|-pw6wI;H!#vSGSUUA&@HaaD@m--%_~-h7y>iLCAB!YD6^m>Ge1uO zWNu78#cK=AJ8M%RJqhnmdxzyH*G*OTMsSR&=U*px?Q^a9L9a zm)(EI4rO5%*&7-v3WidqYEt{(-`hOv)3=@H-!+wfdmM6gU2OGLyYG6Dn|6FkEZjc( zpJjox4g3G*$O7Io4W$l08Fb!$5&OAu&&Tbp-^2_Wy8c%kt2egeR62brvZu`Taf&!UP2#@M%+)97oVb(vE6p=tF2Gecb-#ebe&Pcc!&pw-{=uHkP^z z+Z6k-Z}c_zKjok1ujqqX3j41$2E*lmG*^G?1eVP*d1 zen7i!+c&spu+I9(I&s(a3a<9s4U-3e4Hd~fTXRW9@AuD7Z<)~Zq@vnn_2l_3we z+w2>wv%gkeNqhNLqyP1&+ZP0;*%VDgJ&^ZJD*xCw>bnj=vJ%<(>=RS8AOAR;nkDu)KYU8nZHcSr8@Xqt ziHr0fU{+{1`rcKS_iFW*aAsANzWbTaR`vd!t+mawC(9tx<*@OCVuh7gr+T|LK7apv z)=JqP(SE(U+f5%?mK?R|4CFl)n{K{r=X{R>`$-r7t_pl3?sIp^mVkTOo3J3KK8b=hgF?$>;tcDyl{&D8j6hX3L}*X8T{tSno#d*x4L{AHi~vb#EWUbyGk zlIMP&|EsO)^tQ;iyxw!4XNpKb`IS$5&dxdasq)6H7cIuRYIFP3H+|*gI^Ou?ThSj` z8z%c^`GeCHj7%g%Utj${q41PB_p!+6=+&pc_dT7lWZk0mj?!~3Uu`^A z_EcSPT-mdKI;Vst0L&}-U;qFB literal 0 HcmV?d00001 diff --git a/Resources/Assets.xcassets/Link.imageset/Link@2x.png b/Resources/Assets.xcassets/Link.imageset/Link@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..50fd1b54f933a4bce73fa22a3c448a5ea122b821 GIT binary patch literal 3673 zcmZ`+2UJr@7k-Gu(4^=hAcP136oD9`QUnA<2r7u6(xgc!k&r+LZBa_V1q>Y*1nG+# z5T!S%A}weL(lG&v%7TU72?BrE_1}Nbp7XzR=FQAE-*@lad*{tLFUH)=SddSW4*&o` z6O^F^r$_BIFo^TMFGr$po9Y8Vc>@1;XCBTR>VmQ`1%RIw03a+90N5NT>@xuP zodSTbP5_|w2mp?FXVzcS<}C1fqilQufd9~5;{q}=#Q|U+KjykM(c1KimJ8lX)ftVy z?W!8+<;`IOfOeo3XXxchbcP0cVR61%fjY8(GPF43Js2(v{gXoU)RDC|HHTin6I`KZ zR8Om(lGWvdLZR9Ow40WNq0tXHXQm_TK_q%>!QlY`0jdE=RXo8Rj?mQ9gr8D_tEs7Q z7%IL&IHGf)3eH#VFC+i8W9aJZLcn+vF?bww&#v=rydP0VR(8+P-_KwBBx2nD=LzTg zBP>pU@Vyl{LiH5u@Lt{oS6^R_ zOc(J(@=xsF{Hp|vD<{4^H-Gd0iT#^z?tv%bIk_faTug98R|3b`5A}aH`2QI{Y-z*y z()Ew@{Z*SkQBHAm`LyAG7h0E3=%P{z=X}(e80udSxF(Q~CMQ;6QnkD3m=rl)6&?BF58dg`dmt zdvRX`>e#t43t`^Egx6}b&B^6bD^!aVw0Gh%0XQaA5)mD~pZ>_%FwjV_BJUo`mN&7! zDOlyQ_ZeS*#nfw)dSL!ivTll2)O;Un(dkU#c5KvJhMP_DPI3so-$G{`MsYKH3>;}& zD<3o2)LU3&+3WxM#$waD?7Rj${G$&COU0cv}Z@p+j z8b$I~(tjwAcYIC#B?YGW2_4U55%;w;9}Ss*mCCwTB+7k^laeW(J!ZPcHUVgS|Q1)c|Ei2lqEsNsF&m+(3TO?YJ@g)bwl9U>I$0WX=KGk7w zF0Fp~9de;lJn3+)m|t(6T%|_(^ey_65cG$(4jNNttxlqtt6;FQcd4ifo;65Y(#r`* z^dm3Rx%F42)E?7o27=#*uT)w^ROSFbMN~I~DZ$7KI5SZb#0q=Hz$?cFC;IztH9MJ>^F; z!h^;=t*Xmb5wl$RyAEC}H!NPHyYHJBQa)qG#2xh_EPx{-GSj$Yw+15PpZ>Ccjs}&T zF&+u3es*u?gonIdr%nniN`|Q&KzsPLjdp&xTKA6#efI_zLjqf~4G~$j<#36a(kdhQ zT##GZsGx=#=E)t|wRj5g{!%>p+kkaXc2is{`#NOG<#{hnB1&o=L=WoS^k3*^HR=Bp za-?2!16L*~Ue#)AwOc9SdszGV^#-0%ivOT_^mflM8 zRxKQuBU#MsoJ%{~Ows7&ggDSfoN5n%)olNfNZm^N$Vm`cbg7q_2J~ZE*1v0FDWYX)dXf# zRk~RYINV!QRCOmwTAgzkxh6CjLv2Nn*6{lTn`dcjW}We}T|CK2xRcYKBdj7BFnikw zd-sb&N({i<>OQXqe5!xN zhv3%iu--(akkq@1aTM;^!Ofbytt89JQU=62HPcQq>bFckHJlW|+XH0(SdCxN!=!{i zZ*0CTHLBgR^;6th-iKRYr|i|It4PbK_BDpxoJ-tqYwJR0O^31`cF8MVX!s~mmCPV> ztqC-%QkM#Ibd6T6wvZ_U_WG)|>f!5hU9q-3;*clQ0?gNq*>tkBSnF8`ab2Dq@|}F` zQ#E-C^8%^2GEr>mbMLtCDTV?ytj=>p-Cte(XA9TF@7UM9lnvz3kpW#=b!WD-Po{@| z@L&79V#p>A&ikAzy3h-o8b2&`@awCXh>MR!+Vy|udR^T@-?C@x{&ajO!OQYX4q7Z8 z{8XzdB)9yl80IPRM0ClTZWMd+v>I z<9{px=}h}C>2?A4!+!R?qlh1#Kge!!M$%vKlY5`TG(G0TGALXXr>1q5J*^y+?aScl zo>`{OnTT0U%Msq+vw0**Qj@FMUq#V&169HuH+>sER)4ed^2uGPO8$M)}R}<~628NXM zA%$qCT96EmE&sfT{)}p?<|e;k$oQlSfmpIg*1(ay^2FXYIAKD_V;NPFG*&TnZ$(uHTBg0K8x} zoK?cw=28>d)wz7=R;~N9^NHm>0>Ubo0QEJuZL#9XOzhn(72S6PG)|s>>E=H4duF4% zU+lLw2}|B2SdNl#T|+r?cBFB6^yW6TPmwX(b5-nQoa4b2!4!B0GCUn|#+sRJvKyQM z?k?~d$ntT6>_Xr2mN_CR?3BzjH8-oUM?p;tp5}V<=2}n^`fVc7G$!HYi%LcrS!>qM zS-pTx;n%IQiYb~+tVHTPjdOUz)4X*2B2ExoF}GU}9O=lKzH%q$Kw>}cvSmQ{x>0a5 zhQfdt#xq6F9aTyu9=|61EXqGv1gFkOIIzy64r*+<{z0Ut6qw*M12H5nhK%35e8KFa zW5aU6@<0y>lqG^Tkblro3c5QA5gdIgRgz3n@TBE3a56s^)!g`~d6kwg{bez#SRYmn zt#BTFbu2O@C(`S7p=T%tsfZ{!B-H+`U&$n*#?Jl}Oiw)hKE&w3a@8B=a=#Qc#coBF+ot$cjEt3v{8>}#kRW^s5`XFP<(H-IX)7{XI;`6t+RmdYdLHR;9CXrA(2fHzDGkx-e%j?7ff;&K7_OLU`%>!Mfd&^y(*R ayWGsKvj;uL|Df&tz?ob$Gb}N1BK-$~jkvG? literal 0 HcmV?d00001 diff --git a/Resources/Assets.xcassets/Link.imageset/Link@3x.png b/Resources/Assets.xcassets/Link.imageset/Link@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..c9e2f47d1dbababe153463d9460d6c3ea4c6ca21 GIT binary patch literal 5898 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 literal 0 HcmV?d00001