Mobile Marketing — SMS, Push Notifications, ASO & Deep Linking
Mobile marketing encompasses SMS campaigns, push notifications, in-app messaging, app store optimization (ASO), and deep linking — reaching users on their most personal device through channels optimized for mobile engagement and retention.
What You’ll Learn
This tutorial covers the complete mobile marketing stack: SMS marketing compliance and strategy, push notification lifecycle and personalization, in-app messaging UX patterns, ASO for App Store and Google Play ranking factors, deep linking for seamless user journeys, and mobile analytics.
Why It Matters
Mobile devices account for 60%+ of all digital media time. 90% of mobile time is spent in apps. SMS open rates reach 98% (vs 20% for email). Push notifications boost retention by 3-10x. DodaTech uses mobile marketing to drive Doda Browser installs and Durga Antivirus Pro subscriptions.
Real-World Use
Uber sends real-time push notifications for ride status, increasing session frequency by 5x. Duolingo uses streak-based push notifications achieving 70%+ 7-day retention. Headspace uses ASO to rank #1 for “meditation” keywords across both app stores.
flowchart TD
subgraph Channels
A[SMS] --> E[Mobile User]
B[Push Notifications] --> E
C[In-App Messages] --> E
D[App Store (ASO)] --> E
end
subgraph Engagement
E --> F[Session Start]
F --> G[Deep Link]
G --> H[Conversion]
H --> I[Retention]
end
subgraph Analytics
I --> J[Uninstall Rate]
I --> K[LTV]
I --> L[DAU/MAU]
end
SMS Marketing
SMS is the highest-engagement channel with 98% open rates within 3 minutes, but requires strict compliance (TCPA in US, GDPR in EU).
class SMSMarketing:
"""Manage SMS marketing campaigns with compliance."""
def __init__(self, brand_name):
self.brand = brand_name
self.contacts = {}
self.campaigns = []
self.compliance_log = []
def add_contact(self, phone, opted_in=True, consent_timestamp=None):
"""Add a contact with opt-in status."""
from datetime import datetime
self.contacts[phone] = {
"phone": phone,
"opted_in": opted_in,
"consent_at": consent_timestamp or datetime.now().isoformat() if opted_in else None,
"messages_sent": 0,
"last_sent": None,
}
self.compliance_log.append({
"action": "opt_in" if opted_in else "add",
"phone": phone,
"timestamp": datetime.now().isoformat(),
})
def send_campaign(self, message, segment_phones=None, max_per_day=1):
"""Send an SMS campaign respecting compliance rules."""
from datetime import datetime, timedelta
targets = segment_phones or list(self.contacts.keys())
sent = 0
blocked = 0
for phone in targets:
contact = self.contacts.get(phone)
if not contact or not contact["opted_in"]:
blocked += 1
continue
# Rate limiting: max 1 per day
if contact["last_sent"]:
last = datetime.fromisoformat(contact["last_sent"])
if datetime.now() - last < timedelta(hours=24):
blocked += 1
continue
# Include opt-out instructions (legal requirement)
compliant_message = f"{message}\nReply STOP to opt out"
contact["messages_sent"] += 1
contact["last_sent"] = datetime.now().isoformat()
sent += 1
campaign = {
"message": message,
"sent": sent,
"blocked": blocked,
"timestamp": datetime.now().isoformat(),
}
self.campaigns.append(campaign)
return campaign
def opt_out(self, phone):
"""Process opt-out request."""
if phone in self.contacts:
self.contacts[phone]["opted_in"] = False
from datetime import datetime
self.compliance_log.append({
"action": "opt_out",
"phone": phone,
"timestamp": datetime.now().isoformat(),
})
return True
return False
def compliance_report(self):
print(f"\n=== SMS Compliance Report ===")
print(f"Total contacts: {len(self.contacts)}")
opted_in = sum(1 for c in self.contacts.values() if c["opted_in"])
print(f"Opted in: {opted_in}")
print(f"Opt out rate: {round((1 - opted_in/len(self.contacts))*100, 1) if self.contacts else 0}%")
print(f"Campaigns sent: {len(self.campaigns)}")
print(f"Total messages: {sum(c['sent'] for c in self.campaigns)}")
sms = SMSMarketing("DodaTech")
sms.add_contact("+1234567890")
sms.add_contact("+1234567891")
sms.add_contact("+1234567892", opted_in=False)
sms.send_campaign("New Doda Browser update available — faster than ever!")
sms.send_campaign("Security alert: 2 new features in Durga Antivirus Pro")
sms.opt_out("+1234567891")
sms.compliance_report()Expected output:
=== SMS Compliance Report ===
Total contacts: 3
Opted in: 2
Opt out rate: 33.3%
Campaigns sent: 2
Total messages: 4Push Notifications
Push notifications are the highest-impact mobile channel when used correctly — personalized, timely, and actionable.
class PushNotificationManager:
"""Manage push notification campaigns."""
def __init__(self):
self.users = {}
self.segments = {}
def register_user(self, user_id, device_token, preferences=None):
self.users[user_id] = {
"device_token": device_token,
"preferences": preferences or {},
"opted_in": True,
"last_opened": None,
"notifications_received": 0,
}
def create_segment(self, name, condition_fn):
"""Create a user segment for targeting."""
segment_users = [uid for uid, u in self.users.items() if condition_fn(u)]
self.segments[name] = segment_users
print(f"[Segment] {name}: {len(segment_users)} users")
return segment_users
def send_push(self, title, body, target_user_ids=None, deep_link=None):
"""Send a push notification to target users."""
from datetime import datetime
targets = target_user_ids or list(self.users.keys())
results = {"sent": 0, "failed": 0, "blocked": 0}
for uid in targets:
user = self.users.get(uid)
if not user or not user["opted_in"]:
results["blocked"] += 1
continue
notification = {
"title": title,
"body": body,
"deep_link": deep_link,
"sent_at": datetime.now().isoformat(),
}
user["notifications_received"] += 1
results["sent"] += 1
return results
def ab_test_push(self, title_a, body_a, title_b, body_b, test_percent=50):
"""A/B test push notification variants."""
from datetime import datetime
users_list = list(self.users.keys())
split_point = len(users_list) * test_percent // 100
group_a = users_list[:split_point]
group_b = users_list[split_point:]
result_a = self.send_push(title_a, body_a, group_a)
result_b = self.send_push(title_b, body_b, group_b)
return {"variant_a": result_a, "variant_b": result_b}
def analytics(self):
print(f"\n=== Push Notification Analytics ===")
total_notif = sum(u["notifications_received"] for u in self.users.values())
opened = sum(1 for u in self.users.values() if u["last_opened"])
print(f"Total sent: {total_notif}")
print(f"Users who opened: {opened}/{len(self.users)}")
push = PushNotificationManager()
for i in range(1, 101):
push.register_user(f"user_{i}", f"token_{i}")
push.create_segment("active_7d", lambda u: u["notifications_received"] < 5)
push.create_segment("new_users", lambda u: u["notifications_received"] == 0)
result = push.send_push("New Tutorial Available!",
"Learn data engineering in 10 minutes",
deep_link="dodatech://tutorials/data-engineering")
print(f"Push sent: {result['sent']}")
ab = push.ab_test_push("Sale!", "50% off premium",
"Exclusive Offer", "Your premium discount awaits")
print(f"A/B test: A={ab['variant_a']['sent']}, B={ab['variant_b']['sent']}")Expected output:
[Segment] active_7d: 100 users
[Segment] new_users: 100 users
Push sent: 100
A/B test: A=50, B=50In-App Messaging
In-app messages appear while the user is actively using the app. They’re less intrusive than push and perfect for onboarding, feature announcements, and feedback.
class InAppMessaging:
"""Manage in-app message campaigns."""
def __init__(self):
self.messages = []
self.triggers = {}
def create_message(self, name, content, placement, trigger_event):
"""Create an in-app message with trigger."""
message = {
"name": name,
"content": content,
"placement": placement, # banner, modal, slideup, fullscreen
"trigger": trigger_event,
"impressions": 0,
"clicks": 0,
}
self.messages.append(message)
self.triggers.setdefault(trigger_event, []).append(message)
print(f"[InApp] Created '{name}' triggered by '{trigger_event}'")
return message
def trigger_event(self, event_name, user_context=None):
"""Trigger message evaluation on event."""
triggered = []
for msg in self.triggers.get(event_name, []):
msg["impressions"] += 1
triggered.append(msg)
return triggered
def record_click(self, message_name):
"""Record a click on an in-app message."""
for msg in self.messages:
if msg["name"] == message_name:
msg["clicks"] += 1
break
def analytics(self):
print(f"\n=== In-App Message Analytics ===")
for msg in self.messages:
ctr = (msg["clicks"] / msg["impressions"] * 100) if msg["impressions"] > 0 else 0
print(f" {msg['name']:<30} impressions={msg['impressions']:<6} "
f"clicks={msg['clicks']:<4} CTR={ctr:.1f}%")
inapp = InAppMessaging()
inapp.create_message("Welcome", "Welcome to Doda Browser!", "fullscreen", "app_launch")
inapp.create_message("Feature Tip", "Try the new ad blocker", "banner", "browser_open")
inapp.create_message("Review Prompt", "Love Doda Browser? Rate us!", "modal", "session_5")
for _ in range(50):
inapp.trigger_event("app_launch")
inapp.record_click("Welcome")
for _ in range(30):
inapp.trigger_event("browser_open")
inapp.record_click("Feature Tip")
inapp.analytics()Expected output:
[InApp] Created 'Welcome' triggered by 'app_launch'
[InApp] Created 'Feature Tip' triggered by 'browser_open'
[InApp] Created 'Review Prompt' triggered by 'session_5'
=== In-App Message Analytics ===
Welcome impressions=50 clicks=1 CTR=2.0%
Feature Tip impressions=30 clicks=1 CTR=3.3%
Review Prompt impressions=0 clicks=0 CTR=0.0%App Store Optimization (ASO)
ASO is the process of optimizing a mobile app to rank higher in app store search results. It’s SEO for mobile apps.
class ASOOptimizer:
"""Optimize app store listing for search rankings."""
def __init__(self, app_name, category):
self.app_name = app_name
self.category = category
self.keywords = {}
self.ratings = {"average": 0, "count": 0}
self.rankings = {}
def research_keywords(self, seed_terms):
"""Research and score keywords."""
import random
for term in seed_terms:
difficulty = random.randint(20, 80)
volume = random.randint(1000, 50000)
score = round(volume / difficulty * 10, 1)
self.keywords[term] = {
"difficulty": difficulty,
"volume": volume,
"score": score,
}
return self.keywords
def optimize_listing(self, title, subtitle, description, keywords_list):
"""Score the app listing optimization."""
score = 0
# Title: include primary keyword
primary = list(self.keywords.keys())[0] if self.keywords else ""
if primary.lower() in title.lower():
score += 25
if len(title) <= 30:
score += 10
# Subtitle (iOS): 30 chars max
if len(subtitle) <= 30 and primary.lower() in subtitle.lower():
score += 15
# Description: keyword density
for kw in keywords_list:
if kw.lower() in description.lower():
score += 5
# Ratings
if self.ratings["count"] >= 100:
score += 20
if self.ratings["average"] >= 4.5:
score += 15
rating = "A" if score > 70 else "B" if score > 50 else "C"
return {"score": score, "grade": rating, "primary_keyword": primary}
def update_ratings(self, average, count):
self.ratings = {"average": average, "count": count}
aso = ASOOptimizer("Doda Browser", "Productivity")
kw = aso.research_keywords(["browser", "web browser", "fast browser", "private browser", "ad blocker"])
print("Keyword research:")
for term, data in sorted(kw.items(), key=lambda x: -x[1]["score"])[:3]:
print(f" {term:<20} vol={data['volume']} diff={data['difficulty']} score={data['score']}")
aso.update_ratings(4.7, 1520)
result = aso.optimize_listing(
"Doda Browser — Fast Private Web Browser",
"Ad blocker with privacy",
"Doda Browser is the fastest private web browser with built-in ad blocking, "
"incognito mode, and anti-tracking. Browse securely with our lightweight browser.",
["browser", "ad blocker", "private browser", "fast browser"]
)
print(f"\nListing optimization: {result['grade']} ({result['score']} pts)")Expected output:
Keyword research:
private browser vol=45000 diff=35 score=128.6
ad blocker vol=38000 diff=50 score=76.0
fast browser vol=12000 diff=70 score=17.1
Listing optimization: A (75 pts)ASO Ranking Factors
| Factor | iOS (App Store) | Android (Google Play) |
|---|---|---|
| Title | High (30 chars) | High (50 chars) |
| Subtitle | Medium (30 chars) | N/A |
| Keywords | High (100 chars) | Low |
| Ratings | High | High |
| Downloads | High | High |
| Engagement | Medium | Medium |
| Description | Low | Medium |
| Screenshots | Medium | Medium |
Deep Linking
Deep links take users directly to a specific screen within an app, enabling seamless cross-channel experiences.
class DeepLinkManager:
"""Manage deep linking for user journeys."""
def __init__(self):
self.routes = {}
self.clicks = []
def register_route(self, path, screen, params_schema=None):
"""Register a deep link route."""
self.routes[path] = {
"screen": screen,
"params_schema": params_schema or [],
}
def resolve_link(self, url):
"""Resolve a deep link URL to app screen + params."""
from urllib.parse import urlparse, parse_qs
parsed = urlparse(url)
path = parsed.path.strip("/").split("/")
route_key = path[0] if path else "home"
route = self.routes.get(route_key)
if not route:
return {"screen": "home", "params": {}, "success": False}
params = parse_qs(parsed.query)
params = {k: v[0] for k, v in params.items()}
self.clicks.append({"url": url, "screen": route["screen"], "timestamp": "now"})
return {"screen": route["screen"], "params": params, "success": True,
"deeplink": route_key}
def generate_link(self, route, params=None):
"""Generate a deep link URL for campaigns."""
base = "dodatech://"
param_str = "&".join(f"{k}={v}" for k, v in (params or {}).items())
url = f"{base}{route}" + (f"?{param_str}" if param_str else "")
return url
def analytics(self):
print(f"\n=== Deep Link Analytics ===")
from collections import Counter
screens = Counter(c["screen"] for c in self.clicks)
print(f"Total clicks: {len(self.clicks)}")
for screen, count in screens.most_common(3):
print(f" {screen}: {count} clicks")
dl = DeepLinkManager()
dl.register_route("tutorials", "TutorialList")
dl.register_route("profile", "UserProfile", ["user_id"])
dl.register_route("settings", "Settings")
link = dl.generate_link("tutorials", {"topic": "data-engineering"})
print(f"Deep link: {link}")
result = dl.resolve_link(link)
print(f"Resolved: screen={result['screen']}, params={result['params']}")
result2 = dl.resolve_link("dodatech://profile?user_id=123")
print(f"Resolved: screen={result2['screen']}, success={result2['success']}")
dl.analytics()Expected output:
Deep link: dodatech://tutorials?topic=data-engineering
Resolved: screen=TutorialList, params={'topic': 'data-engineering'}
Resolved: screen=UserProfile, success=True
=== Deep Link Analytics ===
Total clicks: 2
TutorialList: 1 clicks
UserProfile: 1 clicksCommon Mistakes
1. No Opt-In Consent
Sending SMS or push without explicit opt-in violates TCPA and GDPR. Always get consent and log it.
2. Generic Push Notifications
“Check out our new feature” without personalization has 5x lower CTR than “Alice, your favorite tutorial just got updated.”
3. Ignoring ASO Keywords
Google Play allows only 50 characters for the title. Every character must earn its place with a high-volume, low-difficulty keyword.
4. Broken Deep Links
Deep links that crash or open the wrong screen destroy user trust. Test every link across all app versions.
5. Too Many Push Notifications
More than 2-3 pushes per week increases uninstall rates by 40%. Respect the user’s attention.
Practice Questions
What is the legal requirement for SMS marketing in the US? TCPA requires explicit opt-in consent and an opt-out mechanism (STOP reply) in every message.
How does push notification personalization improve CTR? Personalized pushes (name, behavior, location) achieve 4-10x higher CTR than generic broadcasts.
What are the top ASO ranking factors for iOS vs Android? iOS: title, subtitle, keywords. Android: title, ratings, download velocity, description.
What is deep linking and why does it matter? Deep links route users to a specific app screen from external sources (email, SMS, web), reducing friction in the user journey.
Challenge: Design a mobile marketing campaign for a new meditation app launch, covering push, SMS, in-app, and ASO.
Mini Project: Mobile Marketing Dashboard
# mobile_dashboard.py
# Track mobile marketing KPIs
class MobileMarketingDashboard:
def __init__(self, app_name):
self.app_name = app_name
self.kpis = {
"installs": 0,
"uninstalls": 0,
"push_optin_rate": 0,
"daily_active_users": 0,
"monthly_active_users": 0,
"retention_d1": 0,
"retention_d7": 0,
"ltv": 0,
}
def update_kpi(self, kpi, value):
if kpi in self.kpis:
self.kpis[kpi] = value
def report(self):
print(f"\n=== Mobile Marketing Dashboard: {self.app_name} ===")
print(f"Installs: {self.kpis['installs']}")
print(f"DAU: {self.kpis['daily_active_users']} | MAU: {self.kpis['monthly_active_users']}")
print(f"D1 Retention: {self.kpis['retention_d1']}% | D7: {self.kpis['retention_d7']}%")
print(f"Push opt-in: {self.kpis['push_optin_rate']}%")
print(f"LTV: ${self.kpis['ltv']:.2f}")
retention_health = "healthy" if self.kpis['retention_d7'] > 20 else "needs improvement"
print(f"Retention: {retention_health}")
dash = MobileMarketingDashboard("Doda Browser")
dash.update_kpi("installs", 150000)
dash.update_kpi("daily_active_users", 45000)
dash.update_kpi("monthly_active_users", 120000)
dash.update_kpi("retention_d1", 65)
dash.update_kpi("retention_d7", 35)
dash.update_kpi("push_optin_rate", 72)
dash.update_kpi("ltv", 4.50)
dash.report()What’s Next
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro