App全局配置表设计

简介: 运营过程中,我们会碰到需要以App版本为维度,去定制一些App配置变量

概述

运营过程中,我们会碰到需要以App版本为维度,去定制一些App配置变量。如版本更新描述,App最低可用版本等。有时也会需要有动态变更一些配置的能力,比如客诉反馈邮箱,促销活动入口开闭等。

为此,我们设计了App全局配置表模块,即可是满足集中管理App配置,也能满足App配置动态变更的需求。

配置的管理与下发

我们需要为客户端提供一个的服务端API,用于配置表的下发,多为JSON表的形式。

下面是一个简单的实现,客户端上报版本号和请求时间,服务端可以根据版本号来选择下发的配置表。

随着配置表的演进,服务端会把配置迁移到数据库存储,再衍生出后台进行可视化的配置管理。

type AppGlobalConfigRequest struct {
   
    BuildVersion string `json:"build_version"  binding:"required"`
    AppVersion   string `json:"app_version"  binding:"required"`
    Time         string `json:"time"  binding:"required"`
}

func AppGlobalConfig(w http.ResponseWriter, r *http.Request) {
   
  var reqBody map[string]interface{
   }
    if err := json.NewDecoder(r.Body).Decode(&reqBody); err != nil {
   
        log.Printf("AppGlobalConfig reqBody json.NewDecoder: %v", err)
        http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
        return
    }

    var reqParams AppGlobalConfigRequest
    data := reqBody["data"]
    dataJSONString, _ := json.Marshal(data)
    if err := json.Unmarshal([]byte(dataJSONString), &reqParams); err != nil {
   
        log.Printf("AppGlobalConfig reqParams json.Unmarshal: %v", err)
        http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
        return
    }

    config := map[string]interface{
   }{
   
        "version":                  2022091901,
        "user_initial_trial_count": 2,
        "create_date":              "2022-09-19T02:58:31Z",
    }
    response := map[string]interface{
   }{
   
        "data": config,
    }
    responseString, _ := json.Marshal(response)
    fmt.Fprintf(w, string(responseString))
}

配置的更新与持久化

配置的更新

为了保证配置的实效性,App初始化时,以及App从后台唤醒时,都需要检测配置的更新。

class AppConfigModule: SpaceportModuleProtocol {
   
    var loaded = false

    static func modulePriority() -> Int {
    return 2895 }

    func loadModule() {
   
        AppConfigManager.shared.fetchGlobalConfig()
    }

    func unloadModule() {
    }

    func applicationWillEnterForeground(notification: Notification) {
   
        AppConfigManager.shared.fetchGlobalConfig()
    }
}

配置表模型

配置表里包含了三个字段,其中userInitialTrialCount是配置信息,configVersioncreateDate均为配置表的描述信息。

本地的配置表需要有默认值。当出现API请求失败,或者API配置表解析错误时,可以用默认配置保证功能正常使用。

private let defaultUserInitialTrialCount = 2
public struct GlobalConfig: Codable {
   
    /// 配置版本号
    let configVersion: Int
    /// 用户初始试用次数
    let userInitialTrialCount: Int
    /// 生成时间 如:2022-09-19T02:58:31Z
    let createDate: String

    enum CodingKeys: String, CodingKey {
   
        case configVersion = "version"
        case userInitialTrialCount = "user_initial_trial_count"
        case createDate = "create_date"
    }

    init() {
   
        self.configVersion = 0
        self.userInitialTrialCount = defaultUserInitialTrialCount
        self.createDate = Date().axv.toISO8601String()
    }
}

配置表持久化

配置表的持久化和一般模型的持久化基本相同,可以选择Sqlite,JSON File,甚至内存缓存的形式,可根据配置表的数据规模和响应要求来选择。

目前我们选用UserDefault来管理配置表的持久化,即用Plist文件存储的配置表信息。

为了避免配置表频繁持久化,需要对配置的版本进行筛选,低于当前环境的版本可以被抛弃。

enum AppConfig: String {
   
    /// App全局配置 GlobalConfig
    case globalConfig
}

private let appConfigUserDefaults = UserDefaultsManager(suiteName: "com.pe.AppConfig").userDefaults

extension AppConfig: UserDefaultPreference {
   
    var userDefaults: UserDefaults {
    return appConfigUserDefaults }
}

class AppConfigManager {
   
    public static let shared = AppConfigManager()
    private init() {
   }

    lazy var globalConfig: GlobalConfig = {
   
        guard let config = AppConfig.globalConfig.codableObject(GlobalConfig.self) else {
   
            return GlobalConfig()
        }
        return config
    }() {
   
        didSet {
   
            AppConfig.globalConfig.save(codableObject: globalConfig)
        }
    }

    /// 获取配置
    func fetchGlobalConfig() {
   
        Task {
   
            do {
   
                if let config = try await GlobalConfigRequest().request() {
   
                    if config.configVersion > globalConfig.configVersion {
   
                        globalConfig = config
                        logDebug("GlobalConfig Updated: \(globalConfig)", tag: .kLogTag)
                    }
                }
            } catch {
   
                logWarn("got error: \(error)", tag: .kLogTag)
            }
        }
    }
}

总结

配置表下发是一种常用的App配置动态管理方案。

设计方案时需要满足快速的响应速度,便捷的存取API,方便维护的模型,以及对机能的低消耗。

根据业务场景的不同,还可以扩展出配置表版本管理,多级配置表的关联使用,配置信息变更通知等不同的不同的功能点。

目录
相关文章
|
3月前
create-react-app配置环境变量
create-react-app配置环境变量
117 0
|
3月前
|
小程序 开发工具 git
【微信小程序】-- uni-app 项目--- 购物车 -- 配置 tabBar 效果(五十一)
【微信小程序】-- uni-app 项目--- 购物车 -- 配置 tabBar 效果(五十一)
|
3月前
|
移动开发
钉钉H5微应用配置IP,应用首页地址报错:app url exceeds max length limit,这个怎么处理?
钉钉H5微应用配置IP,应用首页地址报错:app url exceeds max length limit,这个怎么处理?
511 0
|
3月前
|
XML Java 测试技术
『App自动化测试之Appium应用篇』| 元素定位工具Appium-Inspector从简介、安装、配置到使用的完整攻略
『App自动化测试之Appium应用篇』| 元素定位工具Appium-Inspector从简介、安装、配置到使用的完整攻略
531 3
|
3月前
|
小程序 Android开发 iOS开发
uni-app 安装与配置
uni-app 安装与配置
41 1
|
3天前
|
C++
【Azure Logic App】使用Event Hub 连接器配置 Active Directory OAuth 认证无法成功连接到中国区Event Hub
在尝试使用Azure Logic App创建由Event Hub触发的工作流时,配置了Active Directory OAuth认证但仍遇到认证失败的问题。错误信息提示找不到指定的租户ID。尽管已设置了正确的Azure中国环境Authority,认证请求似乎仍指向全球Azure环境。这可能是Logic App服务本身的局限导致。作为替代方案,可采用Connection String或Managed Identity方式进行认证,两者均可正常工作。此外,通过Azure Function App复现此问题,进一步验证这是服务层面而非配置问题。相关文档和教程可在Azure官方文档中找到。
|
18天前
|
XML Android开发 UED
"掌握安卓开发新境界:深度解析AndroidManifest.xml中的Intent-filter配置,让你的App轻松响应scheme_url,开启无限交互可能!"
【8月更文挑战第2天】在安卓开发中,scheme_url 通过在`AndroidManifest.xml`中配置`Intent-filter`,使应用能响应特定URL启动或执行操作。基本配置下,应用可通过定义特定URL模式的`Intent-filter`响应相应链接。
48 12
|
3月前
|
开发框架 移动开发 小程序
【微信小程序】-- 配置uni-app的开发环境(四十八)
【微信小程序】-- 配置uni-app的开发环境(四十八)
|
3月前
|
Web App开发 JSON 小程序
苹果app开发apple-app-site-association文件配置
apple-app-site-association 是苹果的配置文件,用于建立app和网站关联,支持Universal Links,使点击网站链接能直接打开相应app内部页面。配置文件为JSON格式,需上传至服务器`.well-known`目录或根目录。通过检查三个链接来测试配置,确保Content-Type为`application/json`。成功配置后,点击链接能在iPhone备忘录或Safari中直接唤起app,但可能有24-48小时延迟。
534 6
|
3月前
|
前端开发 Android开发 iOS开发
应用研发平台EMAS使用 aliyun-react-native-push 库接入推送和辅助通道,推送都可以收到,但是在App切到后台或者杀掉进程之后就收不到推送了,是需要配置什么吗?
【2月更文挑战第31天】应用研发平台EMAS使用 aliyun-react-native-push 库接入推送和辅助通道,推送都可以收到,但是在App切到后台或者杀掉进程之后就收不到推送了,是需要配置什么吗?
71 2

热门文章

最新文章