Fabric, Crashlytic , Firebase - poloteh/iOSRoadmapTutorial GitHub Wiki

Fabric & Crashlytic

Add fabric in your project

Follow the setup Crashlytics

*** When adding run script, you may face the problem that Fabric cannot detect the run script

Solution: For Xcode 10 search "debug information format" in build setting and use DWARF with dSYM file.

Firebase

Import Firebase in cocoapod

Remote Config

Add remote config for MOST project (some are not needed)

To support multiple environments, add a folder as a reference with your Gooogle service plist.

Screenshot 2019-05-07 at 4 42 17 PM

Example code to add in AppDelegate


extension AppDelegate {
    
    private func configureFirebaseAnalytics() {
        var filePath:String!
        //        #if DEBUG || STAGING
        //        print("[FIREBASE] Development mode.")
        //        filePath = Bundle.main.path(forResource: "GoogleService-Info", ofType: "plist", inDirectory: "Staging")
        //        #else
        print("[FIREBASE] Production mode.")
        filePath = Bundle.main.path(forResource: "GoogleService-Info", ofType: "plist")
        //        #endif
        
        let options = FirebaseOptions.init(contentsOfFile: filePath)!
        FirebaseApp.configure(options: options)
    }
    
    private func configureFirebaseremoteConfigsWithLaunchOptions(launchOptions: [UIApplicationLaunchOptionsKey: Any]?) {
        // [START get_remote_config_instance]
        remoteConfig = RemoteConfig.remoteConfig()
        // [END get_remote_config_instance]
        // Create a Remote Config Setting to enable developer mode, which you can use to increase
        // the number of fetches available per hour during development. See Best Practices in the
        // README for more information.
        // [START enable_dev_mode]
        remoteConfig.configSettings = RemoteConfigSettings(developerModeEnabled: true)
        // [END enable_dev_mode]
        // Set default Remote Config parameter values. An app uses the in-app default values, and
        // when you need to adjust those defaults, you set an updated value for only the values you
        // want to change in the Firebase console. See Best Practices in the README for more
        // information.
        // [START set_default_values]
        //        remoteConfig.setDefaults(fromPlist: "RemoteConfigDefaults")
        // [END set_default_values]
        
        fetchFirebaseRemoteConfig()
    }

    func startAppCondinator(){
    }

    func fetchFirebaseRemoteConfig() {
        
        var expirationDuration = 3600
        // If your app is using developer mode, expirationDuration is set to 0, so each fetch will
        // retrieve values from the service.
        if remoteConfig.configSettings.isDeveloperModeEnabled {
            expirationDuration = 0
        }
        
        // [START fetch_config_with_callback]
        // TimeInterval is set to expirationDuration here, indicating the next fetch request will use
        // data fetched from the Remote Config service, rather than cached parameter values, if cached
        // parameter values are more than expirationDuration seconds old. See Best Practices in the
        // README for more information.
        remoteConfig.fetch(withExpirationDuration: TimeInterval(expirationDuration)) { (status, error) -> Void in
            if status == .success {
                print("Config fetched!")
                self.remoteConfig.activateFetched()
            } else {
                print("Config not fetched")
                print("Error: \(error?.localizedDescription ?? "No error available.")")
                self.startAppCondinator()
                return
            }
            
            let forceUpdate = self.remoteConfig[FirebaseRemoteConfigKeys.FORCEUPDATEENABLEDIOS].boolValue
            let serverVersion = self.remoteConfig[FirebaseRemoteConfigKeys.IOSVERSION].stringValue
            let updateMessage = forceUpdate ?  self.remoteConfig[FirebaseRemoteConfigKeys.FORCEUPDATEMESSAGE].stringValue : self.remoteConfig[FirebaseRemoteConfigKeys.UPDATEMESSAGE].stringValue
            self.checkUpdateVersion(updateMessage: updateMessage!, serverVersion: serverVersion!, forceUpdate: forceUpdate)
        }
        // [END fetch_config_with_callback]
    }
    
    func checkUpdateVersion(updateMessage:String, serverVersion:String, forceUpdate:Bool) {
        if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
            if serverVersion.compare(version, options: .numeric) == .orderedDescending {
                showUpdateAlert(message: updateMessage, forceUpdate: forceUpdate)
            } else {
                startAppCondinator()
            }
        } else {
            startAppCondinator()
        }
    }
    
    func showUpdateAlert(message:String, forceUpdate:Bool) {
        let alertController = UIAlertController(title: "App update request", message: message, preferredStyle: .actionSheet)
        let okAction = UIAlertAction(title: "Update", style: UIAlertActionStyle.default) {
            UIAlertAction in
            
            #if DEBUG || STAGING
            let downloadURL = AppURLs.stagingURL
            #else
            let downloadURL = AppURLs.appStoreURL
            #endif
            
            #if DEBUG || STAGING
            UIApplication.shared.open(URL(string: downloadURL.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!)!, options:[:], completionHandler: { (success) in
                exit(0)
            })
            #else
            if let url = URL(string: "itms-apps://" + downloadURL),
                UIApplication.shared.canOpenURL(url){
                UIApplication.shared.open(url, options:[:], completionHandler: { (success) in
                    exit(0)
                })
            } else {
                UIApplication.shared.open(URL(string: downloadURL.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!)!, options:[:], completionHandler: { (success) in
                    exit(0)
                })
            }
            #endif
        }
        let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel) {
            UIAlertAction in
            if (forceUpdate) {
                exit(0)
            } else {
                self.startAppCondinator()
            }
        }
        
        alertController.addAction(okAction)
        alertController.addAction(cancelAction)
        self.window?.rootViewController?.present(alertController, animated: true, completion: nil)
    }
    
}

enum FirebaseRemoteConfigKeys {
    static let FORCEUPDATEMESSAGE = "force_update_message"
    static let UPDATEMESSAGE = "update_message"
    static let IOSVERSION = "app_version"
    static let FORCEUPDATEENABLEDIOS = "force_update_enabled"
}

enum AppURLs {
    static let appStoreURL = "https://itunes.apple.com/us/app/(APPNAME)/(APP id)"
    static let stagingURL = "(Own Server)"
}