From df72aa02317b663af438107427a49c3b1f4278ed Mon Sep 17 00:00:00 2001 From: Umair Date: Tue, 20 Feb 2024 00:24:04 +0000 Subject: [PATCH 1/5] [DRAFT] adds code for channels push & fixes push example to work with simulators --- .../AblyPushExample.xcodeproj/project.pbxproj | 14 +++--- .../AblyPush/AblyPushExample/AblyHelper.swift | 40 +++++++++++++++++ .../AblyPushExample/ContentView.swift | 45 ++++++++++++++++++- Source/ARTPushActivationStateMachine.m | 2 - 4 files changed, 92 insertions(+), 9 deletions(-) diff --git a/Examples/AblyPush/AblyPushExample.xcodeproj/project.pbxproj b/Examples/AblyPush/AblyPushExample.xcodeproj/project.pbxproj index aa2be5061..eb83141a6 100644 --- a/Examples/AblyPush/AblyPushExample.xcodeproj/project.pbxproj +++ b/Examples/AblyPush/AblyPushExample.xcodeproj/project.pbxproj @@ -458,10 +458,11 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = AblyPushExample/AblyPushExample.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"AblyPushExample/Preview Content\""; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = XXY98AVDR6; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = AblyPushExample/Info.plist; @@ -491,10 +492,11 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = AblyPushExample/AblyPushExample.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"AblyPushExample/Preview Content\""; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = XXY98AVDR6; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = AblyPushExample/Info.plist; @@ -526,7 +528,7 @@ CODE_SIGN_ENTITLEMENTS = AblyLocationPush/AblyLocationPush.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = XXY98AVDR6; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = AblyLocationPush/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = AblyLocationPush; @@ -554,7 +556,7 @@ CODE_SIGN_ENTITLEMENTS = AblyLocationPush/AblyLocationPush.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = XXY98AVDR6; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = AblyLocationPush/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = AblyLocationPush; @@ -585,7 +587,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"AblyPushExample/Preview Content\""; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = XXY98AVDR6; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = AblyPushExample/InfoLP.plist; @@ -621,7 +623,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"AblyPushExample/Preview Content\""; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = XXY98AVDR6; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = AblyPushExample/InfoLP.plist; diff --git a/Examples/AblyPush/AblyPushExample/AblyHelper.swift b/Examples/AblyPush/AblyPushExample/AblyHelper.swift index 547ae9857..bdfa91fae 100644 --- a/Examples/AblyPush/AblyPushExample/AblyHelper.swift +++ b/Examples/AblyPush/AblyPushExample/AblyHelper.swift @@ -18,6 +18,7 @@ class AblyHelper: NSObject { var activatePushCallback: ((String?, String?, ARTErrorInfo?) -> ())? + private override init() { super.init() guard key != "" else { @@ -87,6 +88,40 @@ extension AblyHelper { print("Publish result: \(error?.localizedDescription ?? "Success")") } } + + func sendPushToChannel(_ channel: Channel) { + let message = ARTMessage(name: "example", data: "rest data") + message.extras = [ + "push": [ + "notification": [ + "title": "Channel Push", + "body": "Sent push to \(channel.rawValue)" + ], + "data": [ + "foo": "bar", + "baz": "qux" + ] + ] + ] as any ARTJsonCompatible + + realtime.channels.get(channel.rawValue).publish([message]) { error in + if let error { + print("Error sending push to \(channel.rawValue) with error: \(error.localizedDescription)") + } else { + print("Sent push to \(channel.rawValue)") + } + } + } + + func subscribeToChannel(_ channel: Channel) { + realtime.channels.get(channel.rawValue).push.subscribeDevice { error in + if let error { + print("Error subscribing to \(channel.rawValue) with error: \(error.localizedDescription)") + } else { + print("Succesfully subscribed to \(channel.rawValue)") + } + } + } } extension AblyHelper: ARTPushRegistererDelegate { @@ -141,3 +176,8 @@ extension AblyHelper : CLLocationManagerDelegate { } } } + +enum Channel: String { + case exampleChannel1 + case exampleChannel2 +} diff --git a/Examples/AblyPush/AblyPushExample/ContentView.swift b/Examples/AblyPush/AblyPushExample/ContentView.swift index aad0bf210..0317ca6d5 100644 --- a/Examples/AblyPush/AblyPushExample/ContentView.swift +++ b/Examples/AblyPush/AblyPushExample/ContentView.swift @@ -11,6 +11,7 @@ struct ContentView: View { @State var defaultDeviceToken: String? @State var locationDeviceToken: String? @State var deviceTokensError: ARTErrorInfo? + @Binding var subscribedToExampleChannel1: Bool var body: some View { NavigationView { @@ -42,6 +43,16 @@ struct ContentView: View { AblyHelper.shared.deactivatePush() } .padding() + PushButton(title: "Remember Me", isOn: $subscribedToExampleChannel1) + + Button("Subscribe to \(Channel.exampleChannel1.rawValue)") { + AblyHelper.shared.subscribeToChannel(.exampleChannel1) + } + .padding() + Button("Subscribe to \(Channel.exampleChannel2.rawValue)") { + AblyHelper.shared.subscribeToChannel(.exampleChannel2) + } + .padding() Button("Print Token") { AblyHelper.shared.printIdentityToken() } @@ -63,10 +74,18 @@ struct ContentView: View { return Alert(title: Text("Device Details Error"), message: Text("Unknown result.")) } .padding() - Button("Send Push") { + Button("Send Push to deviceId") { AblyHelper.shared.sendAdminPush(title: "Hello", body: "This push was sent with deviceId") } .padding() + Button("Send Push to \(Channel.exampleChannel1.rawValue)") { + AblyHelper.shared.sendPushToChannel(.exampleChannel1) + } + .padding() + Button("Send Push to \(Channel.exampleChannel2.rawValue)") { + AblyHelper.shared.sendPushToChannel(.exampleChannel2) + } + .padding() #if USE_LOCATION_PUSH NavigationLink { LocationPushEventsView() @@ -87,3 +106,27 @@ struct ContentView_Previews: PreviewProvider { ContentView() } } + + +struct PushButton: View { + let title: String + @Binding var isOn: Bool + + var onColors = [Color.red, Color.yellow] + var offColors = [Color(white: 0.6), Color(white: 0.4)] + + var body: some View { + Button(title) { + isOn.toggle() + } + .padding() + .background(LinearGradient( + colors: isOn ? onColors : offColors, + startPoint: .top, endPoint: .bottom + ) + ) + .foregroundStyle(.white) + .clipShape(.capsule) + .shadow(radius: isOn ? 0 : 5) + } +} diff --git a/Source/ARTPushActivationStateMachine.m b/Source/ARTPushActivationStateMachine.m index 5d3679d22..3701a527a 100644 --- a/Source/ARTPushActivationStateMachine.m +++ b/Source/ARTPushActivationStateMachine.m @@ -400,11 +400,9 @@ - (void)callUpdatedCallback:(nullable ARTErrorInfo *)error { } - (void)registerForAPNS { -#if !TARGET_OS_SIMULATOR dispatch_async(dispatch_get_main_queue(), ^{ [[UIApplication sharedApplication] registerForRemoteNotifications]; }); -#endif } @end From 7a6de9727e0e43379cb204ba25d0e93c86aea8f8 Mon Sep 17 00:00:00 2001 From: Umair Date: Thu, 22 Feb 2024 20:48:47 +0000 Subject: [PATCH 2/5] UX work to bring the push example more in line with the Ably Design System. --- .../AblyPushExample.xcodeproj/project.pbxproj | 20 ++ .../AblyPush/AblyPushExample/AblyHelper.swift | 43 +++- .../ably-logo.imageset/Contents.json | 32 +++ .../ably-brand-dark-mode.pdf | Bin 0 -> 6949 bytes .../ably-brand-light-mode 1.pdf | Bin 0 -> 6949 bytes .../ably-brand-light-mode.pdf | Bin 0 -> 6949 bytes .../push-disabled.imageset/Contents.json | 12 ++ .../push-disabled.imageset/push-disabled.pdf | Bin 0 -> 6578 bytes .../push-enabled.imageset/Contents.json | 12 ++ .../push-enabled.imageset/push-enabled.pdf | Bin 0 -> 6108 bytes .../AblyPushExample/Buttons/PushButton.swift | 52 +++++ .../Buttons/StatelessButton.swift | 48 +++++ .../Buttons/VerticalLabelStyle.swift | 10 + .../AblyPushExample/ContentView.swift | 197 +++++++++++------- 14 files changed, 349 insertions(+), 77 deletions(-) create mode 100644 Examples/AblyPush/AblyPushExample/Assets.xcassets/ably-logo.imageset/Contents.json create mode 100644 Examples/AblyPush/AblyPushExample/Assets.xcassets/ably-logo.imageset/ably-brand-dark-mode.pdf create mode 100644 Examples/AblyPush/AblyPushExample/Assets.xcassets/ably-logo.imageset/ably-brand-light-mode 1.pdf create mode 100644 Examples/AblyPush/AblyPushExample/Assets.xcassets/ably-logo.imageset/ably-brand-light-mode.pdf create mode 100644 Examples/AblyPush/AblyPushExample/Assets.xcassets/push-disabled.imageset/Contents.json create mode 100644 Examples/AblyPush/AblyPushExample/Assets.xcassets/push-disabled.imageset/push-disabled.pdf create mode 100644 Examples/AblyPush/AblyPushExample/Assets.xcassets/push-enabled.imageset/Contents.json create mode 100644 Examples/AblyPush/AblyPushExample/Assets.xcassets/push-enabled.imageset/push-enabled.pdf create mode 100644 Examples/AblyPush/AblyPushExample/Buttons/PushButton.swift create mode 100644 Examples/AblyPush/AblyPushExample/Buttons/StatelessButton.swift create mode 100644 Examples/AblyPush/AblyPushExample/Buttons/VerticalLabelStyle.swift diff --git a/Examples/AblyPush/AblyPushExample.xcodeproj/project.pbxproj b/Examples/AblyPush/AblyPushExample.xcodeproj/project.pbxproj index eb83141a6..66a370156 100644 --- a/Examples/AblyPush/AblyPushExample.xcodeproj/project.pbxproj +++ b/Examples/AblyPush/AblyPushExample.xcodeproj/project.pbxproj @@ -8,6 +8,9 @@ /* Begin PBXBuildFile section */ 219F937A2A4E0E44007DE767 /* LocationPushEventsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 219F93792A4E0E44007DE767 /* LocationPushEventsView.swift */; }; + 801880802B87E7BA00A698F0 /* PushButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8018807F2B87E7BA00A698F0 /* PushButton.swift */; }; + 801880822B87E80D00A698F0 /* StatelessButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 801880812B87E80D00A698F0 /* StatelessButton.swift */; }; + 801880872B87E8A300A698F0 /* VerticalLabelStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 801880862B87E8A300A698F0 /* VerticalLabelStyle.swift */; }; 8405EE8427F0B06800C9E461 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8405EE8327F0B06800C9E461 /* App.swift */; }; 8405EE8627F0B06800C9E461 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8405EE8527F0B06800C9E461 /* ContentView.swift */; }; 8405EE8827F0B06C00C9E461 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8405EE8727F0B06C00C9E461 /* Assets.xcassets */; }; @@ -52,6 +55,9 @@ /* Begin PBXFileReference section */ 219F93782A4DFE41007DE767 /* AblyLocationPush.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AblyLocationPush.entitlements; sourceTree = ""; }; 219F93792A4E0E44007DE767 /* LocationPushEventsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationPushEventsView.swift; sourceTree = ""; }; + 8018807F2B87E7BA00A698F0 /* PushButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushButton.swift; sourceTree = ""; }; + 801880812B87E80D00A698F0 /* StatelessButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatelessButton.swift; sourceTree = ""; }; + 801880862B87E8A300A698F0 /* VerticalLabelStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerticalLabelStyle.swift; sourceTree = ""; }; 8405EE8027F0B06800C9E461 /* Ably Push.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Ably Push.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 8405EE8327F0B06800C9E461 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = ""; }; 8405EE8527F0B06800C9E461 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -95,6 +101,16 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 801880852B87E86600A698F0 /* Buttons */ = { + isa = PBXGroup; + children = ( + 801880812B87E80D00A698F0 /* StatelessButton.swift */, + 8018807F2B87E7BA00A698F0 /* PushButton.swift */, + 801880862B87E8A300A698F0 /* VerticalLabelStyle.swift */, + ); + path = Buttons; + sourceTree = ""; + }; 8405EE7727F0B06800C9E461 = { isa = PBXGroup; children = ( @@ -122,6 +138,7 @@ 8405EE8327F0B06800C9E461 /* App.swift */, 842F124927F0B29D0029692E /* AblyHelper.swift */, 8405EE8527F0B06800C9E461 /* ContentView.swift */, + 801880852B87E86600A698F0 /* Buttons */, 219F93792A4E0E44007DE767 /* LocationPushEventsView.swift */, 842F124B27F0BFC10029692E /* Info.plist */, 84E3D1A32A6D886E0081E898 /* InfoLP.plist */, @@ -300,7 +317,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 801880822B87E80D00A698F0 /* StatelessButton.swift in Sources */, 219F937A2A4E0E44007DE767 /* LocationPushEventsView.swift in Sources */, + 801880872B87E8A300A698F0 /* VerticalLabelStyle.swift in Sources */, + 801880802B87E7BA00A698F0 /* PushButton.swift in Sources */, 8405EE8627F0B06800C9E461 /* ContentView.swift in Sources */, 842F124A27F0B29D0029692E /* AblyHelper.swift in Sources */, 8405EE8427F0B06800C9E461 /* App.swift in Sources */, diff --git a/Examples/AblyPush/AblyPushExample/AblyHelper.swift b/Examples/AblyPush/AblyPushExample/AblyHelper.swift index bdfa91fae..f40d33ce8 100644 --- a/Examples/AblyPush/AblyPushExample/AblyHelper.swift +++ b/Examples/AblyPush/AblyPushExample/AblyHelper.swift @@ -2,7 +2,7 @@ import Ably import UIKit import CoreLocation -class AblyHelper: NSObject { +class AblyHelper: NSObject, ObservableObject { static let shared = AblyHelper() @@ -18,6 +18,9 @@ class AblyHelper: NSObject { var activatePushCallback: ((String?, String?, ARTErrorInfo?) -> ())? + @Published var isSubscribedToExampleChannel1 = false + @Published var isSubscribedToExampleChannel2 = false + @Published var isPushActivated = false private override init() { super.init() @@ -115,10 +118,34 @@ extension AblyHelper { func subscribeToChannel(_ channel: Channel) { realtime.channels.get(channel.rawValue).push.subscribeDevice { error in - if let error { - print("Error subscribing to \(channel.rawValue) with error: \(error.localizedDescription)") - } else { - print("Succesfully subscribed to \(channel.rawValue)") + guard error == nil else { + print("Error subscribing to \(channel.rawValue) with error: \(error!.localizedDescription)") + return + } + print("Succesfully subscribed to \(channel.rawValue)") + + switch channel { + case .exampleChannel1: + self.isSubscribedToExampleChannel1 = true + case .exampleChannel2: + self.isSubscribedToExampleChannel2 = true + } + } + } + + func unsubscribeFromChannel(_ channel: Channel) { + realtime.channels.get(channel.rawValue).push.unsubscribeDevice { error in + guard error == nil else { + print("Error subscribing to \(channel.rawValue) with error: \(error!.localizedDescription)") + return + } + + print("Succesfully unsubscribed from \(channel.rawValue)") + switch channel { + case .exampleChannel1: + self.isSubscribedToExampleChannel1 = false + case .exampleChannel2: + self.isSubscribedToExampleChannel2 = false } } } @@ -130,10 +157,16 @@ extension AblyHelper: ARTPushRegistererDelegate { print("Push activation: \(error?.localizedDescription ?? "Success")") activatePushCallback?(defaultDeviceToken, locationDeviceToken, error) activateLocationPush() + if error == nil { + isPushActivated = true + } } func didDeactivateAblyPush(_ error: ARTErrorInfo?) { print("Push deactivation: \(error?.localizedDescription ?? "Success")") + if error == nil { + isPushActivated = false + } } func didUpdateAblyPush(_ error: ARTErrorInfo?) { diff --git a/Examples/AblyPush/AblyPushExample/Assets.xcassets/ably-logo.imageset/Contents.json b/Examples/AblyPush/AblyPushExample/Assets.xcassets/ably-logo.imageset/Contents.json new file mode 100644 index 000000000..6a2ca6ce7 --- /dev/null +++ b/Examples/AblyPush/AblyPushExample/Assets.xcassets/ably-logo.imageset/Contents.json @@ -0,0 +1,32 @@ +{ + "images" : [ + { + "filename" : "ably-brand-light-mode 1.pdf", + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "light" + } + ], + "filename" : "ably-brand-light-mode.pdf", + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "ably-brand-dark-mode.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/AblyPush/AblyPushExample/Assets.xcassets/ably-logo.imageset/ably-brand-dark-mode.pdf b/Examples/AblyPush/AblyPushExample/Assets.xcassets/ably-logo.imageset/ably-brand-dark-mode.pdf new file mode 100644 index 0000000000000000000000000000000000000000..63621a4d980ae18d9b5a23287076daf022269015 GIT binary patch literal 6949 zcmeHMOK%%D5WerP;3YtE@QNJ1fxtjxCq>Z)b?x4Y9u#?FtI)%hn)$l=Ao8ls z!{QdoXo^OW=zq!J$lLDM_5EE|_U0fybnxBwZsK7iyuW{!N3QQzdC~dx^~B^Xe*zYa zGcpPuko5ZU0A-Dr{eWKeG~lCHz89TZWVMx29A`r>e6$UPJZw-Q#Ha=vly}C9q|!*6 z!D#DT^vMR&R_6hQ4aOfYQ>?;C#}WC@BvpYzUhC(KhtLN83=y z!v-%kslmO~q%VyJsNliyun$^kUl^TiNO|XL@sI?E; zT(5j936qoHM8dQQ=7f1MKOCCfrmZ$FsQvSHe%LJ^P#bDh;{^B43vFvdfr1n#`7ysH zS375-(&9wL+89GpiN20(tJd+??CN%Yz1-ZitDSAPyX$>fi6*a0*()KRpR-(HcD7w@ zcUO1wMI&bC&0ot!bNT(NuJSGACZ1_`+g9fo^5KC*@YT=m(_LjT2l}<*3oDp-3a+)1e@Aqx{x)(4P4&I@p{R z#MxC<{95d*KdVm@4^FneW=iiusEzhUN1?f0^Q6RLorfm5^srxSD(!0{oedYick<&Ix2BD57CW#-Kp}%izfrsIj%uT0ujckqY&I z4y=E_n>Z~c2=UbEKcI=xa_0#xXi#W{1=c}_Z;TRPIS`2Og+>_zqe=IS7M79ib@36CK5A5I1!p!1z9EGTc}isM#4&=!f`Rs zCvz~-rd17gy4Wq3^_>JeBiQ5MXDFOB$6Hc(U7*_4-cyA4tSX{9AOJYx5K-FT`h-XQTdv*lhlQ9Czx^;N3gZgp@kmO7!-py@y0Ex1akc1f&lcU z*}#4ulzG{VEh;F8O_I1i%P0(iqQMvr-=GYdD-Jm1g*NdS-YtX6ss$4lNQcqLu@P z9(5aXEWE^eIB$QDwue?Rr%@J{up9)DE4+xrn}@?ACMIPQ5Gaxq#0%+l<@vbtoT$sy zJhlKuWJHA2X*rxU2tS3(($R7g)Bb115ZT5eh2@z0gfVIva|X@^A42K@b;0p{BgcTW zO5!>cj5r^oCa_a5)=REHEy@V_$k;p_0UB|B;Ivs)78KyH#ranfGqHX)cH$zAYpJfU zByDV}yF!Awul#Pa*&fi=^>uox>u#E^WthEfu9x#yTkbQHRp|i*N*$#qTmVD6zVaHT zx@`8_``x113vOkH|BIY$H)x2kB-eifcro7r6S0SiDv_bi2-fz;W!r+X3711_fiChM zV^y#3(DvLkdmzP3sMX4;F6NL(E26^fO+T9-=Bw?^cE0@3x=^2U)T@O zVK-l{nq9^8{&0?6e!kzXn-}8w^7=(b@$7QD1;0#RyI-@brNgNubw LJ$v^0=QsZXIXEIX literal 0 HcmV?d00001 diff --git a/Examples/AblyPush/AblyPushExample/Assets.xcassets/ably-logo.imageset/ably-brand-light-mode 1.pdf b/Examples/AblyPush/AblyPushExample/Assets.xcassets/ably-logo.imageset/ably-brand-light-mode 1.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f96dc92f6ce722f43025ab63eb47fd61bef3a175 GIT binary patch literal 6949 zcmeHMOK%%D5WerP;3YtE@QNJ1fxtjxCq>Z)b?x4Y9u#?FtI)%hn)$l=Ao8ls z!{QdoXo^OW=zq!J$lLDM_5EE|_U0fybnxBwZsK7iyuW{!N3QQzdC~dx^~B^Xe*zYa zGcpPuko5ZU0A-Dr{eWKeG~lCHz89TZWVMx29A`r>e6$UPJZw-Q#Ha=vly}C9q|!*6 z!D#DT^vMR&R_6hQ4aOfYQ>?;C#}WC@BvpYzUhC(KhtLN83=y z!v-%kslmO~q%VyJsNliyun$^kUl^TiNO|XL@sI?E; zT(5j936qoHM8dQQ=7f1MKOCCfrmZ$FsQvSHe%LJ^P#bDh;{^B43vFvdfr1n#`7ysH zS375-(&9wL+89GpiN20(tJd+??CN%Yz1-ZitDSAPyX$>fi6*a0*()KRpR-(HcD7w@ zcUO1wMI&bC&0ot!bNT(NuJSGACZ1_`+g9fo^5KC*@YT=m(_LjT2l}<*3oDp-3a+)1e@Aqx{x)(4P4&I@p{R z#MxC<{95d*KdVm@4^FneW=iiusEzhUN1?f0^Q6RLT|tgZY$RC>XxjxmSQ(Kz_lr%X zeQl((frOHEutq5~c?D{zc4#tDuCFR%5EhY8iBF@L%gPXJE6dMlu!7JWOp0O@7Nvi* zgyP+(nxfQE3TtZg9VAO^Q8_|-chZ?c(naKSLO0CGU!nm;O3xSq(JN!M7Z{x+m8-QS zhZXaTuv4i(rd&H?iwasgg{~?I<{uP91UpV zb5-z}SzcAQY^PGuSTC~v>jKph+7LQT@-$Dm|=NU@$wug_GuZOA4-@-Rrxr{9;Uz2u{x)AOJIR){jjbLk|Lkm4XPz>J0 z8@J$`)yZ-EB71?}G#l9OgEB9hu|)+1u}KowXBmYdP&63B;Tx1ebHxFNywD~-!@Fg0 zS+!sy1{C3|wQ~VPXsXFt_5;j0fo3W}+62KZ@d(>Ud=&$UK2?GEjAA>Cme_BESoB1= zyv*T(CpZeYC@|Q{j!Fh)Zk9ye#n@c=K?0#Kfd*0s=*n zf_Ndlt~?)io)dMsn#UHPh>VDkIxUBj2H~f0Svp#7V%q=A7$VzPq_7-wpD;!(W6r?Y z;6q3~pe{JRZ{!$|R-tYXV8rbG@9u+H#+ntV$0k zQ0gc>;Q|=i_1S5d>ay8y?{|x4FSwN*{x5R2-Jl`Dl3f1{;Kh6gOvD~4szio5BUsxX zmu(BmCR`4!1-i(4j8(n9L)&xH?12VP73 literal 0 HcmV?d00001 diff --git a/Examples/AblyPush/AblyPushExample/Assets.xcassets/ably-logo.imageset/ably-brand-light-mode.pdf b/Examples/AblyPush/AblyPushExample/Assets.xcassets/ably-logo.imageset/ably-brand-light-mode.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f96dc92f6ce722f43025ab63eb47fd61bef3a175 GIT binary patch literal 6949 zcmeHMOK%%D5WerP;3YtE@QNJ1fxtjxCq>Z)b?x4Y9u#?FtI)%hn)$l=Ao8ls z!{QdoXo^OW=zq!J$lLDM_5EE|_U0fybnxBwZsK7iyuW{!N3QQzdC~dx^~B^Xe*zYa zGcpPuko5ZU0A-Dr{eWKeG~lCHz89TZWVMx29A`r>e6$UPJZw-Q#Ha=vly}C9q|!*6 z!D#DT^vMR&R_6hQ4aOfYQ>?;C#}WC@BvpYzUhC(KhtLN83=y z!v-%kslmO~q%VyJsNliyun$^kUl^TiNO|XL@sI?E; zT(5j936qoHM8dQQ=7f1MKOCCfrmZ$FsQvSHe%LJ^P#bDh;{^B43vFvdfr1n#`7ysH zS375-(&9wL+89GpiN20(tJd+??CN%Yz1-ZitDSAPyX$>fi6*a0*()KRpR-(HcD7w@ zcUO1wMI&bC&0ot!bNT(NuJSGACZ1_`+g9fo^5KC*@YT=m(_LjT2l}<*3oDp-3a+)1e@Aqx{x)(4P4&I@p{R z#MxC<{95d*KdVm@4^FneW=iiusEzhUN1?f0^Q6RLT|tgZY$RC>XxjxmSQ(Kz_lr%X zeQl((frOHEutq5~c?D{zc4#tDuCFR%5EhY8iBF@L%gPXJE6dMlu!7JWOp0O@7Nvi* zgyP+(nxfQE3TtZg9VAO^Q8_|-chZ?c(naKSLO0CGU!nm;O3xSq(JN!M7Z{x+m8-QS zhZXaTuv4i(rd&H?iwasgg{~?I<{uP91UpV zb5-z}SzcAQY^PGuSTC~v>jKph+7LQT@-$Dm|=NU@$wug_GuZOA4-@-Rrxr{9;Uz2u{x)AOJIR){jjbLk|Lkm4XPz>J0 z8@J$`)yZ-EB71?}G#l9OgEB9hu|)+1u}KowXBmYdP&63B;Tx1ebHxFNywD~-!@Fg0 zS+!sy1{C3|wQ~VPXsXFt_5;j0fo3W}+62KZ@d(>Ud=&$UK2?GEjAA>Cme_BESoB1= zyv*T(CpZeYC@|Q{j!Fh)Zk9ye#n@c=K?0#Kfd*0s=*n zf_Ndlt~?)io)dMsn#UHPh>VDkIxUBj2H~f0Svp#7V%q=A7$VzPq_7-wpD;!(W6r?Y z;6q3~pe{JRZ{!$|R-tYXV8rbG@9u+H#+ntV$0k zQ0gc>;Q|=i_1S5d>ay8y?{|x4FSwN*{x5R2-Jl`Dl3f1{;Kh6gOvD~4szio5BUsxX zmu(BmCR`4!1-i(4j8(n9L)&xH?12VP73 literal 0 HcmV?d00001 diff --git a/Examples/AblyPush/AblyPushExample/Assets.xcassets/push-disabled.imageset/Contents.json b/Examples/AblyPush/AblyPushExample/Assets.xcassets/push-disabled.imageset/Contents.json new file mode 100644 index 000000000..eca39d820 --- /dev/null +++ b/Examples/AblyPush/AblyPushExample/Assets.xcassets/push-disabled.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "push-disabled.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/AblyPush/AblyPushExample/Assets.xcassets/push-disabled.imageset/push-disabled.pdf b/Examples/AblyPush/AblyPushExample/Assets.xcassets/push-disabled.imageset/push-disabled.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4c4648cb0e32ec34beaac1852922cd91223a380c GIT binary patch literal 6578 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 literal 0 HcmV?d00001 diff --git a/Examples/AblyPush/AblyPushExample/Buttons/PushButton.swift b/Examples/AblyPush/AblyPushExample/Buttons/PushButton.swift new file mode 100644 index 000000000..0b758a2c7 --- /dev/null +++ b/Examples/AblyPush/AblyPushExample/Buttons/PushButton.swift @@ -0,0 +1,52 @@ +import SwiftUI + +struct PushButton: View { + let systemImage: String + let activeTitle: String + let inactiveTitle: String + let action: () -> Void + let isButtonEnabled: Bool // is the button enabled + @Binding var isActive: Bool // is the action in it's on or off state + + let activeColor = Color(red: 1.00, green: 0.33, blue: 0.09) + let inActiveColor = Color(red: 0.00, green: 0.56, blue: 0.02) + + init( + isButtonEnabled: Bool, + systemImage: String, + activeTitle: String, + inactiveTitle: String, + isActive: Binding, + action: @escaping () -> Void + ) { + self.isButtonEnabled = isButtonEnabled + self.systemImage = systemImage + self.activeTitle = activeTitle + self.inactiveTitle = inactiveTitle + self._isActive = isActive + self.action = action + } + + var body: some View { + let button = Button { + action() // manage the toggling of isActive binding within the action. + } label: { + Label(isActive ? activeTitle : inactiveTitle, + systemImage: systemImage) + .frame(maxWidth: .infinity) + .labelStyle(VerticalLabelStyle()) + .padding(EdgeInsets(top: 8, leading: 0, bottom: 8, trailing: 0)) + } + .buttonStyle(.borderedProminent) + .buttonBorderShape(.roundedRectangle) + .tint(isActive ? activeColor : inActiveColor) + .disabled(!isButtonEnabled) + .animation(.easeInOut, value: isButtonEnabled) + + if #available(iOS 17.0, *) { + return button.contentTransition(.symbolEffect(.replace)) + } + + return button + } +} diff --git a/Examples/AblyPush/AblyPushExample/Buttons/StatelessButton.swift b/Examples/AblyPush/AblyPushExample/Buttons/StatelessButton.swift new file mode 100644 index 000000000..bef79f19a --- /dev/null +++ b/Examples/AblyPush/AblyPushExample/Buttons/StatelessButton.swift @@ -0,0 +1,48 @@ +import SwiftUI + +struct StatelessButton: View { + let isButtonEnabled: Bool + let systemImage: String + let title: String + let backgroundColor: Color + let action: () -> Void + + @State var taps: Int = 0 // purely to trigger animation on every tap + + init( + isButtonEnabled: Bool, + systemImage: String, + title: String, + backgroundColor: Color = Color(red: 0.00, green: 0.56, blue: 0.02), + action: @escaping () -> Void + ) { + self.isButtonEnabled = isButtonEnabled + self.systemImage = systemImage + self.title = title + self.backgroundColor = backgroundColor + self.action = action + } + + var body: some View { + let button = Button { + action() + taps += 1 + } label: { + Label(title, systemImage: systemImage) + .frame(maxWidth: .infinity, maxHeight: .infinity) + .labelStyle(VerticalLabelStyle()) + .padding(EdgeInsets(top: 8, leading: 0, bottom: 8, trailing: 0)) + } + .buttonStyle(.bordered) + .buttonBorderShape(.roundedRectangle) + .tint(backgroundColor) + .disabled(!isButtonEnabled) + .animation(.easeInOut, value: isButtonEnabled) + + if #available(iOS 17.0, *) { + return button.symbolEffect(.bounce.down, value: taps) + } + + return button + } +} diff --git a/Examples/AblyPush/AblyPushExample/Buttons/VerticalLabelStyle.swift b/Examples/AblyPush/AblyPushExample/Buttons/VerticalLabelStyle.swift new file mode 100644 index 000000000..418fb27be --- /dev/null +++ b/Examples/AblyPush/AblyPushExample/Buttons/VerticalLabelStyle.swift @@ -0,0 +1,10 @@ +import SwiftUI + +struct VerticalLabelStyle: LabelStyle { + func makeBody(configuration: Configuration) -> some View { + VStack(alignment: .center, spacing: 8) { + configuration.icon + configuration.title + } + } +} diff --git a/Examples/AblyPush/AblyPushExample/ContentView.swift b/Examples/AblyPush/AblyPushExample/ContentView.swift index 0317ca6d5..a7085d06b 100644 --- a/Examples/AblyPush/AblyPushExample/ContentView.swift +++ b/Examples/AblyPush/AblyPushExample/ContentView.swift @@ -2,7 +2,8 @@ import Ably import SwiftUI struct ContentView: View { - + @StateObject private var ablyHelper = AblyHelper.shared + @State var showDeviceDetailsAlert = false @State var deviceDetails: ARTDeviceDetails? @State var deviceDetailsError: ARTErrorInfo? @@ -11,20 +12,35 @@ struct ContentView: View { @State var defaultDeviceToken: String? @State var locationDeviceToken: String? @State var deviceTokensError: ARTErrorInfo? - @Binding var subscribedToExampleChannel1: Bool - + var body: some View { NavigationView { VStack { - Spacer() - Button("Activate Push") { - AblyHelper.shared.activatePush { - defaultDeviceToken = $0 - locationDeviceToken = $1 - deviceTokensError = $2 - showDeviceTokensAlert = true + Image("ably-logo") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(height: 44) + .padding() + + PushButton( + isButtonEnabled: true, // Button to toggle push activation is always enabled + systemImage: ablyHelper.isPushActivated ? "bell" : "bell.slash", + activeTitle: "Deactivate Push", + inactiveTitle: "Activate Push", + isActive: $ablyHelper.isPushActivated, + action: { + if ablyHelper.isPushActivated { + ablyHelper.deactivatePush() + } else { + AblyHelper.shared.activatePush { + defaultDeviceToken = $0 + locationDeviceToken = $1 + deviceTokensError = $2 + showDeviceTokensAlert = true + } + } } - } + ) .alert(isPresented: $showDeviceTokensAlert) { if let deviceTokensError = deviceTokensError { return Alert(title: Text("Device Tokens"), message: Text("Error: \(deviceTokensError)")) @@ -38,32 +54,34 @@ struct ContentView: View { } return Alert(title: Text("Push activation"), message: Text("Success")) } - .padding() - Button("Dectivate") { - AblyHelper.shared.deactivatePush() - } - .padding() - PushButton(title: "Remember Me", isOn: $subscribedToExampleChannel1) - - Button("Subscribe to \(Channel.exampleChannel1.rawValue)") { - AblyHelper.shared.subscribeToChannel(.exampleChannel1) - } - .padding() - Button("Subscribe to \(Channel.exampleChannel2.rawValue)") { - AblyHelper.shared.subscribeToChannel(.exampleChannel2) - } - .padding() - Button("Print Token") { - AblyHelper.shared.printIdentityToken() - } - .padding() - Button("Device Details") { - AblyHelper.shared.getDeviceDetails { details, error in - deviceDetails = details - deviceDetailsError = error - showDeviceDetailsAlert = true - } + + HStack { + let backgroundColor = Color(red: 0.40, green: 0.44, blue: 0.52) + StatelessButton( + isButtonEnabled: ablyHelper.isPushActivated, + systemImage: "printer.fill", + title: "Print Token", + backgroundColor: backgroundColor, + action: { + ablyHelper.printIdentityToken() + } + ) + StatelessButton( + isButtonEnabled: ablyHelper.isPushActivated, + systemImage: "info.circle.fill", + title: "Device Details", + backgroundColor: backgroundColor, + action: { + AblyHelper.shared.getDeviceDetails { details, error in + deviceDetails = details + deviceDetailsError = error + showDeviceDetailsAlert = true + } + } + ) } + .fixedSize(horizontal: false, vertical: true) + .alert(isPresented: $showDeviceDetailsAlert) { if deviceDetails != nil { return Alert(title: Text("Device Details"), message: Text("\(deviceDetails!)")) @@ -73,19 +91,78 @@ struct ContentView: View { } return Alert(title: Text("Device Details Error"), message: Text("Unknown result.")) } - .padding() - Button("Send Push to deviceId") { - AblyHelper.shared.sendAdminPush(title: "Hello", body: "This push was sent with deviceId") - } - .padding() - Button("Send Push to \(Channel.exampleChannel1.rawValue)") { - AblyHelper.shared.sendPushToChannel(.exampleChannel1) + Text("Device Push") + .fontWeight(.bold) + .padding(.top) + StatelessButton( + isButtonEnabled: ablyHelper.isPushActivated, + systemImage: "iphone.gen3", + title: "Send Push to deviceId", + action: { + AblyHelper.shared.sendAdminPush(title: "Hello", body: "This push was sent with deviceId") + } + ) + .fixedSize(horizontal: false, vertical: true) + Text("Channels Push") + .fontWeight(.bold) + .padding(.top) + + HStack { + PushButton( + isButtonEnabled: ablyHelper.isPushActivated, + systemImage: ablyHelper.isSubscribedToExampleChannel1 + ? "checkmark.circle.fill" + : "xmark.circle.fill", + activeTitle: "Unsubscribe from exampleChannel1", + inactiveTitle: "Subscribe to exampleChannel1", + isActive: $ablyHelper.isSubscribedToExampleChannel1, + action: { + if ablyHelper.isSubscribedToExampleChannel1 { + ablyHelper.unsubscribeFromChannel(.exampleChannel1) + } else { + ablyHelper.subscribeToChannel(.exampleChannel1) + } + } + ) + PushButton( + isButtonEnabled: ablyHelper.isPushActivated, + systemImage: ablyHelper.isSubscribedToExampleChannel2 + ? "checkmark.circle.fill" + : "xmark.circle.fill", + activeTitle: "Unsubscribe from exampleChannel2", + inactiveTitle: "Subscribe to exampleChannel2", + isActive: $ablyHelper.isSubscribedToExampleChannel2, + action: { + if ablyHelper.isSubscribedToExampleChannel2 { + ablyHelper.unsubscribeFromChannel(.exampleChannel2) + } else { + ablyHelper.subscribeToChannel(.exampleChannel2) + } + } + ) } - .padding() - Button("Send Push to \(Channel.exampleChannel2.rawValue)") { - AblyHelper.shared.sendPushToChannel(.exampleChannel2) + .fixedSize(horizontal: false, vertical: true) + + HStack { + StatelessButton( + isButtonEnabled: ablyHelper.isPushActivated && ablyHelper.isSubscribedToExampleChannel1, + systemImage: "paperplane.circle.fill", + title: "Send Push to \(Channel.exampleChannel1.rawValue)", + action: { + AblyHelper.shared.sendPushToChannel(.exampleChannel1) + } + ) + StatelessButton( + isButtonEnabled: ablyHelper.isPushActivated && ablyHelper.isSubscribedToExampleChannel2, + systemImage: "paperplane.circle.fill", + title: "Send Push to \(Channel.exampleChannel2.rawValue)", + action: { + AblyHelper.shared.sendPushToChannel(.exampleChannel2) + } + ) } - .padding() + .fixedSize(horizontal: false, vertical: true) + #if USE_LOCATION_PUSH NavigationLink { LocationPushEventsView() @@ -96,7 +173,7 @@ struct ContentView: View { #endif Spacer() } - .navigationTitle("Ably Push Example") + .padding() } } } @@ -106,27 +183,3 @@ struct ContentView_Previews: PreviewProvider { ContentView() } } - - -struct PushButton: View { - let title: String - @Binding var isOn: Bool - - var onColors = [Color.red, Color.yellow] - var offColors = [Color(white: 0.6), Color(white: 0.4)] - - var body: some View { - Button(title) { - isOn.toggle() - } - .padding() - .background(LinearGradient( - colors: isOn ? onColors : offColors, - startPoint: .top, endPoint: .bottom - ) - ) - .foregroundStyle(.white) - .clipShape(.capsule) - .shadow(radius: isOn ? 0 : 5) - } -} From 21f79b08304d800bfe32911772e0f3e0f6dda82b Mon Sep 17 00:00:00 2001 From: Umair Date: Mon, 4 Mar 2024 21:56:36 +0000 Subject: [PATCH 3/5] Extends readme to include information on channels based push --- Examples/AblyPush/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Examples/AblyPush/README.md b/Examples/AblyPush/README.md index 4aec5ea84..625bd90cf 100644 --- a/Examples/AblyPush/README.md +++ b/Examples/AblyPush/README.md @@ -8,10 +8,11 @@ You will need a real iOS device to test this functionality. - Insert it instead of an empty string in the `key` property's value inside the `AblyHelper` class. - Make sure you've selected your development team in the `Signing & Capabilities` tab of the Xcode project target settings and all the provisioning profiles created without errors (update the `bundle-id` for the `AblyPushExample` target as needed). - Build and Run the app on your device. -- Hit "Activate" button, then hit "Print Token". You should see "IDENTITY TOKEN: exists" printed in the debug output window. +- Hit "Activate Push" button, then hit "Print Token". You should see "IDENTITY TOKEN: exists" printed in the debug output window. - Hit "Device Details" to display Ably push device registration info after successful activation. - Go to the `Notifications` tab of the Ably's app dashboard again and scroll to the `Push Inspector` section. Fill in `Title` and `Body` fields of the push notification. Then insert "basic-apns-example" in the `Client ID` field and press enter. Then hit "Push to client" button. You should now see the notification on the screen of your device. -- Also you can send notifications from the app itself if you tick `Push Admin` capability in your API key settings in the app's dashboard. Just hit "Send Push" button to send a predefined push notification which is also will be displayed on your device's screen right away. You can change this behavior in the `UNUserNotificationCenterDelegate` extension for the `AblyHelper` by editing `completionHandler([.banner, .sound])` line of code. +- Also you can send notifications from the app itself if you tick `Push Admin` capability in your API key settings in the app's dashboard. Just hit "Send Push to deviceId" button to send a predefined push notification which is also will be displayed on your device's screen right away. You can change this behavior in the `UNUserNotificationCenterDelegate` extension for the `AblyHelper` by editing `completionHandler([.banner, .sound])` line of code. +- To test Channels based push, go to the `Settings` tab of the Ably dashboard and scroll down to `Channel rules`. Press `Add new rule` and name your channel ID `exampleChannel1`, tick `Push notifications enabled` before scrolling down and pressing `Create channel rule`. Repeat this step for a channel named `exampleChannel2`. You can now subscribe and send pushes to these two channels within the example app. You can also use the Push Inspector. ### Location pushes From 6a68d68f43de37cc0285fd4881e2910ba1818c39 Mon Sep 17 00:00:00 2001 From: Umair Date: Tue, 12 Mar 2024 13:49:39 +0000 Subject: [PATCH 4/5] fixes button icons --- Examples/AblyPush/AblyPushExample/ContentView.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Examples/AblyPush/AblyPushExample/ContentView.swift b/Examples/AblyPush/AblyPushExample/ContentView.swift index a7085d06b..b8591ca54 100644 --- a/Examples/AblyPush/AblyPushExample/ContentView.swift +++ b/Examples/AblyPush/AblyPushExample/ContentView.swift @@ -24,7 +24,7 @@ struct ContentView: View { PushButton( isButtonEnabled: true, // Button to toggle push activation is always enabled - systemImage: ablyHelper.isPushActivated ? "bell" : "bell.slash", + systemImage: ablyHelper.isPushActivated ? "bell.slash" : "bell", activeTitle: "Deactivate Push", inactiveTitle: "Activate Push", isActive: $ablyHelper.isPushActivated, @@ -111,8 +111,8 @@ struct ContentView: View { PushButton( isButtonEnabled: ablyHelper.isPushActivated, systemImage: ablyHelper.isSubscribedToExampleChannel1 - ? "checkmark.circle.fill" - : "xmark.circle.fill", + ? "xmark.circle.fill" + : "checkmark.circle.fill", activeTitle: "Unsubscribe from exampleChannel1", inactiveTitle: "Subscribe to exampleChannel1", isActive: $ablyHelper.isSubscribedToExampleChannel1, @@ -127,8 +127,8 @@ struct ContentView: View { PushButton( isButtonEnabled: ablyHelper.isPushActivated, systemImage: ablyHelper.isSubscribedToExampleChannel2 - ? "checkmark.circle.fill" - : "xmark.circle.fill", + ? "xmark.circle.fill" + : "checkmark.circle.fill", activeTitle: "Unsubscribe from exampleChannel2", inactiveTitle: "Subscribe to exampleChannel2", isActive: $ablyHelper.isSubscribedToExampleChannel2, From 240e927c223ed28d866cb273bfbf838560cecee2 Mon Sep 17 00:00:00 2001 From: Umair Date: Wed, 13 Mar 2024 20:29:12 +0000 Subject: [PATCH 5/5] Removes unnecessary use of `Binding` --- Examples/AblyPush/AblyPushExample/Buttons/PushButton.swift | 6 +++--- Examples/AblyPush/AblyPushExample/ContentView.swift | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Examples/AblyPush/AblyPushExample/Buttons/PushButton.swift b/Examples/AblyPush/AblyPushExample/Buttons/PushButton.swift index 0b758a2c7..192c2785f 100644 --- a/Examples/AblyPush/AblyPushExample/Buttons/PushButton.swift +++ b/Examples/AblyPush/AblyPushExample/Buttons/PushButton.swift @@ -6,7 +6,7 @@ struct PushButton: View { let inactiveTitle: String let action: () -> Void let isButtonEnabled: Bool // is the button enabled - @Binding var isActive: Bool // is the action in it's on or off state + let isActive: Bool // is the action in it's on or off state let activeColor = Color(red: 1.00, green: 0.33, blue: 0.09) let inActiveColor = Color(red: 0.00, green: 0.56, blue: 0.02) @@ -16,14 +16,14 @@ struct PushButton: View { systemImage: String, activeTitle: String, inactiveTitle: String, - isActive: Binding, + isActive: Bool, action: @escaping () -> Void ) { self.isButtonEnabled = isButtonEnabled self.systemImage = systemImage self.activeTitle = activeTitle self.inactiveTitle = inactiveTitle - self._isActive = isActive + self.isActive = isActive self.action = action } diff --git a/Examples/AblyPush/AblyPushExample/ContentView.swift b/Examples/AblyPush/AblyPushExample/ContentView.swift index b8591ca54..1881b4806 100644 --- a/Examples/AblyPush/AblyPushExample/ContentView.swift +++ b/Examples/AblyPush/AblyPushExample/ContentView.swift @@ -27,7 +27,7 @@ struct ContentView: View { systemImage: ablyHelper.isPushActivated ? "bell.slash" : "bell", activeTitle: "Deactivate Push", inactiveTitle: "Activate Push", - isActive: $ablyHelper.isPushActivated, + isActive: ablyHelper.isPushActivated, action: { if ablyHelper.isPushActivated { ablyHelper.deactivatePush() @@ -115,7 +115,7 @@ struct ContentView: View { : "checkmark.circle.fill", activeTitle: "Unsubscribe from exampleChannel1", inactiveTitle: "Subscribe to exampleChannel1", - isActive: $ablyHelper.isSubscribedToExampleChannel1, + isActive: ablyHelper.isSubscribedToExampleChannel1, action: { if ablyHelper.isSubscribedToExampleChannel1 { ablyHelper.unsubscribeFromChannel(.exampleChannel1) @@ -131,7 +131,7 @@ struct ContentView: View { : "checkmark.circle.fill", activeTitle: "Unsubscribe from exampleChannel2", inactiveTitle: "Subscribe to exampleChannel2", - isActive: $ablyHelper.isSubscribedToExampleChannel2, + isActive: ablyHelper.isSubscribedToExampleChannel2, action: { if ablyHelper.isSubscribedToExampleChannel2 { ablyHelper.unsubscribeFromChannel(.exampleChannel2)