MoPromoteKit is a powerful and easy-to-use Swift Package that helps iOS developers cross-promote their other apps. It fetches all apps from the same developer using the iTunes Search API and displays them with beautiful SwiftUI cards, featuring global ratings aggregation from 80+ countries.
- π Global Ratings Aggregation - Combines ratings from 80+ App Store regions for more accurate data
- π― Manual App Selection - Promote specific apps by their IDs for curated collections
- π¨ Beautiful SwiftUI Cards - Ready-to-use cards with SF Symbol category icons
- π€ Developer Profile Images - Add developer avatars from URLs or local assets
- π Hybrid Promotion - Combine featured apps with automatic developer discovery
- π Performance Optimized - Concurrent API calls for fast loading
- π§ Highly Configurable - Customize appearance, limits, and behavior
- π± Multiple Card Styles - Regular, compact, and featured layouts
- π Analytics Insights - Built-in analytics for app performance tracking
- π 80+ Country Support - All major App Store regions included
- π§© Clean Architecture - Modular, testable, and maintainable code
- π Zero Dependencies - Lightweight package with no external dependencies
- File β Add Package Dependencies
- Enter URL:
https://github.com/mkhasson97/MoPromoteKit.git - Add Package
dependencies: [
.package(url: "https://github.com/mkhasson97/MoPromoteKit", from: "1.0.0")
]import SwiftUI
import MoPromoteKit
struct SettingsView: View {
var body: some View {
ScrollView {
VStack(spacing: 16) {
// Your existing settings content
Text("Settings")
.font(.largeTitle)
.fontWeight(.bold)
// Add developer apps section
MoPromoteKit.developerAppsView(currentAppId: 1234567890)
}
}
}
}Configure MoPromoteKit globally in your App file:
import SwiftUI
import MoPromoteKit
@main
struct MyApp: App {
init() {
// Configure MoPromoteKit
MoPromoteKit.configure { config in
config.maxApps = 6
config.cardStyle = .regular
config.showTitle = true
config.countryCode = "us" // Optional: force specific country
}
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}Perfect for adding a "More Apps" section to your settings:
struct SettingsView: View {
var body: some View {
List {
Section("General") {
// Your settings
}
Section {
MoPromoteKit.developerAppsView(currentAppId: 1234567890)
} header: {
Text("More Apps")
}
}
}
}Promote specific apps by their IDs for curated collections:
struct FeaturedAppsView: View {
var body: some View {
ScrollView {
// Manually select which apps to promote
MoPromoteKit.manualAppsView(appIds: [
1234567890, // Your productivity app
9876543210, // Your game
5555555555 // Your utility app
])
}
}
}Combine featured apps with automatic developer discovery:
struct RecommendedAppsView: View {
var body: some View {
ScrollView {
MoPromoteKit.hybridAppsView(
featuredAppIds: [1234567890, 9876543210], // Featured prominently
currentAppId: 5555555555, // Your current app
maxAdditional: 3 // Max additional developer apps
)
}
}
}Add developer profile images to enhance branding:
struct SettingsView: View {
var body: some View {
ScrollView {
// With URL profile image
DeveloperAppsView.forSettings(
currentAppId: 1234567890,
developerProfile: .url("https://yoursite.com/developer-avatar.jpg")
)
// Or with local asset
DeveloperAppsView.forSettings(
currentAppId: 1234567890,
developerProfile: .asset("developer_avatar")
)
}
}
}Use the compact style for sidebars or smaller sections:
struct SidebarView: View {
var body: some View {
VStack {
// Main content
MoPromoteKit.compactDeveloperAppsView(
currentAppId: 1234567890,
maxApps: 3
)
}
}
}For dedicated "More Apps" screens:
struct MoreAppsView: View {
var body: some View {
NavigationView {
ScrollView {
MoPromoteKit.fullScreenDeveloperAppsView(currentAppId: 1234567890)
.padding()
}
.navigationTitle("More Apps")
.navigationBarTitleDisplayMode(.large)
}
}
}Exclude specific apps and customize sorting:
struct CustomAppsView: View {
var body: some View {
ScrollView {
DeveloperAppsView(
currentAppId: 1234567890,
excludeAppIds: [9999999999], // Exclude competitor or deprecated apps
maxApps: 8,
cardStyle: .featured,
sortingOrder: .rating, // Sort by highest rated
showAnalytics: true, // Show analytics cards
developerProfile: .url("https://yoursite.com/avatar.jpg", size: 50)
)
}
}
}Access the data programmatically for custom implementations:
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
Task {
do {
// Fetch specific apps
let specificApps = try await MoPromoteKit.fetchApps(appIds: [
1234567890, 9876543210, 5555555555
])
// Fetch all developer apps with exclusions
let developerApps = try await MoPromoteKit.fetchDeveloperApps(
currentAppId: 1234567890,
excludeAppIds: [9999999999],
includeCurrentApp: false
)
// Get promotion insights
let insights = await MoPromoteKit.getPromotionInsights(appIds: [
1234567890, 9876543210, 5555555555
])
print("Total download potential: \(insights.totalDownloadPotential)")
print("Average rating: \(insights.averageRating)")
print("Top performing app: \(insights.topPerformingApp?.trackName ?? "None")")
print("Recommended order: \(insights.recommendedPromotionOrder)")
} catch {
print("Error: \(error)")
}
}
}
}public struct Configuration {
/// Maximum number of apps to display (default: 10)
public var maxApps: Int = 10
/// Country code for App Store region (default: auto-detected)
public var countryCode: String?
/// Whether to show the "More Apps" title (default: true)
public var showTitle: Bool = true
/// Card display style (default: .regular)
public var cardStyle: CardStyle = .regular
/// Enable global ratings aggregation (default: true)
public var enableGlobalRatings: Bool = true
/// Cache duration for search results in seconds (default: 300)
public var cacheDuration: TimeInterval = 300
/// App selection mode (default: .allFromDeveloper)
public var appSelectionMode: AppSelectionMode = .allFromDeveloper(currentAppId: 0)
/// Sorting order for apps (default: .alphabetical)
public var sortingOrder: SortingOrder = .alphabetical
/// Custom title override
public var customTitle: String?
}public enum CardStyle {
case regular // Standard detailed cards
case compact // Smaller cards for tight spaces
case featured // Large prominent cards with descriptions
}public enum SortingOrder {
case alphabetical // Sort by app name A-Z
case rating // Sort by highest rating first
case releaseDate // Sort by newest first
case downloads // Sort by most reviews/downloads
case random // Random shuffle
case custom([Int]) // Custom order by app IDs
}public enum AppSelectionMode {
case allFromDeveloper(currentAppId: Int)
case manual(appIds: [Int])
case hybrid(featuredAppIds: [Int], currentAppId: Int, maxAdditional: Int)
}// Manual app promotion
MoPromoteKit.configureForManualPromotion(
appIds: [1234567890, 9876543210],
title: "Our Best Apps",
cardStyle: .featured
)
// Hybrid promotion
MoPromoteKit.configureForHybridPromotion(
featuredAppIds: [1234567890, 9876543210],
currentAppId: 5555555555,
maxAdditional: 3
)
// Predefined configurations
MoPromoteKit.configure(.forSettings) // For settings pages
MoPromoteKit.configure(.compact) // For compact displays
MoPromoteKit.configure(.fullScreen) // For full-screen displaysMoPromoteKit's standout feature is global ratings aggregation. Instead of showing ratings from just one country, it:
- Fetches ratings from 30+ major markets concurrently
- Calculates weighted averages based on review counts
- Shows total global review counts for better social proof
- Provides more accurate ratings especially for international apps
You can debug the global ratings feature:
Task {
let debug = await MoPromoteKit.debugGlobalRatings(appId: 1234567890)
print(debug)
}Output example:
π Global Ratings Analysis for App ID: 1234567890
==================================================
π Summary:
β’ Total Reviews: 45,234
β’ Weighted Average: 4.3β
β’ Markets with data: 28/30
π Top Markets:
1. 4.5β (12,543 reviews)
2. 4.4β (8,921 reviews)
3. 4.2β (6,432 reviews)
...
Add professional developer branding with profile images:
DeveloperAppsView.forSettings(
currentAppId: 1234567890,
developerProfile: .url("https://yoursite.com/developer-avatar.jpg")
)DeveloperAppsView.forSettings(
currentAppId: 1234567890,
developerProfile: .asset("developer_avatar") // Image in your app bundle
)DeveloperAppsView.forSettings(
currentAppId: 1234567890,
developerProfile: .url("https://yoursite.com/avatar.jpg", size: 60)
)Get detailed insights about your app promotion performance:
let insights = await MoPromoteKit.getPromotionInsights(appIds: [
1234567890, 9876543210, 5555555555
])
// Access insights data
print("Total reviews across all apps: \(insights.totalDownloadPotential)")
print("Combined average rating: \(insights.averageRating)")
print("Best performing app: \(insights.topPerformingApp?.trackName ?? "None")")
print("Categories represented: \(insights.categoryBreakdown)")
print("Recommended promotion order: \(insights.recommendedPromotionOrder)")You can customize the appearance by modifying the configuration:
MoPromoteKit.configure { config in
config.cardStyle = .featured
config.maxApps = 5
config.showTitle = false
config.sortingOrder = .rating
}Use the convenience modifier for quick integration:
struct MyView: View {
var body: some View {
VStack {
Text("Welcome")
// Other content
}
.withDeveloperApps(currentAppId: 1234567890)
}
}MoPromoteKit supports 80+ App Store regions including:
Major Markets: US, UK, Canada, Australia, Germany, France, Italy, Spain, Netherlands, Sweden, Norway, Denmark, Finland, Japan, South Korea, China, Hong Kong, Taiwan, Singapore, India, Brazil, Mexico...
Complete list available via:
let countries = MoPromoteKit.supportedCountries
let majorMarkets = MoPromoteKit.majorMarkets- iOS 15.0+ / macOS 12.0+ / watchOS 8.0+ / tvOS 15.0+
- Swift 5.9+
- Xcode 15.0+
- Go to App Store Connect
- Select your app
- Go to App Information
- Find Apple ID (e.g., 1234567890)
Or extract it from your App Store URL:
https://apps.apple.com/app/id1234567890
β
This is your App ID
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
MoPromoteKit is available under the MIT license. See the LICENSE file for more info.
MIT License
Copyright (c) 2025 Mohammad Alhasson
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.
Mohammad Alhasson
- Website: mkhasson97.com
- GitHub: @mkhasson97
- X: @mkhasson97
- Apple's iTunes Search API
- SF Symbols for beautiful category icons
- The iOS developer community for feedback and suggestions
If you're using MoPromoteKit in your project, I'd love to hear about it! Feel free to:
- β Star this repository
- π Report issues
- π‘ Suggest new features
- π’ Share your implementations
Made with β€οΈ in Swift
Made with β€οΈ for the iOS developer community