In case your app needs your own notification system, this article will help you understand, how it works.
Read time: 15 min
So, what is a push notification in details? Some
UIView with information, that’s showing on top of any other view in your app. Even when you pushing new screen, it still stays on top.
What else? This
UIView definitely has
UIPanGestureRecogniser to swipe it away or pull down for extra content and
UITapGestureRecogniser to do some work when user tap it.
Sounds pretty easy. But how to make it pin on top of all other views?
Custom Alert UIWindow
Most commonly, your application has only one UIWindow. That one, which you
.makeKeyAndVisible(). Apple SDK says:
The key window receives keyboard and other non-touch related events. Only one window at a time may be the key window.
That means, we can keep our key window and create a new one, above it. We just need override function to detect if touch point belongs to this window. If don’t — touch event will be passed to next underlaying window.
To control our in app push notification animations and actions we need… yes, a controller.
UIViewController can play that role.
If your app don’t use
SceneDelegate, new window initialisation will be easier via
.init(frame: CGRect) . It’s important to set window level
Windows at this level appear on top of the status bar.
(That’s not true since iOS 13)
Also, you may’ve noticed, controller creates it’s own parent window and then sets self as rootViewController. Usually it shouldn’t be like that, but here we have a unique story.
Also, there’s an open question: who should retain reference to this controller? For example, it can be AppDelegate or Router entity. Easiest way is to keep it as
lazy var pushController = PushNotificationsController() .
Firing notification signal
From any place we can fire notification to show our in-app push (just like with common UNNotificationRequest).
Here comes construction of
LocalNotification where I provide it with title, body and userInfo dictionary. Why? Let’s look at the next code snippet.
So, in general
userInfo of LocalNotification should provide observer with all necessary data, to define what to do, when user taps push notification. Usually it’s a segue only.
Showing Push Notification
When we need to show notification, we just call
show(_ notification: LocalNotification) .
CustomNotificationView with all required data. In my example it just provides text for title and subtitle.
Notification window also gets weak reference to a notification button subview.
Layout and slide down animation:
Before animation, notification view should be placed above top window border. Can be done by changing
frame.origin.y equal to negative view height. And then, from that position
frame.origin.y will change to it’s original value inside
UIView.animate closure. For iPhone models with notch this value is
view.safeAreaInsets.top and for other models — 8.
You can check out this implementation in repository (link at the end).
Notice, that on animation completion there is delayed hide notification selector perform. Why don’t we use hide UIView.animation with delay instead? At least, because it messes up notification current frame.
Why? Extra task for you 🔍
That geometry again…
Very simplified work with
UIPanGestureRecognizer , without rubber band movement down. Cancelling pan on halfway up — returns view on it’s place, cancelling further — hides notification before selector delayed perform (and cancels this selector).
Finally, proper work for hiding current notification is animation of moving it up behind top border and in completion remove it from superview and if there’s no new notification it’s better to hide window for now.
App can show any important information and no need to worry if user declines app notification request.
Custom push notification can have any appearance, width, height etc. Only limited by your imagination (or UX/UI designer’s).
When your phone will get real push notification via APNS, in-app push notification view is blocked. In that way, user can miss some information.
Example project repo: https://github.com/TarasEmti/in-app-push-notifications