Skip to content

Sürüm Yönetimi

Mobil uygulamalarda sürüm yönetimi, kullanıcıya kesintisiz deneyim sunmak ve uygulamanın yaşam döngüsünü etkili şekilde yönetmek için kritik öneme sahiptir. Bu dokümantasyon, modern sürüm yönetimi stratejilerini ve CI/CD entegrasyonunu kapsar.

Semantic Versioning

Versiyon Numaralandırma

MAJOR.MINOR.PATCH
Örnek: 2.1.3

MAJOR: Geriye dönük uyumsuz değişiklikler
MINOR: Geriye dönük uyumlu yeni özellikler
PATCH: Geriye dönük uyumlu hata düzeltmeleri

iOS App Store Sürümleme

swift
// Info.plist
<key>CFBundleShortVersionString</key>
<string>2.1.3</string>
<key>CFBundleVersion</key>
<string>23</string>

// Sürüm yönetimi
struct AppVersion {
    static let marketing = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown"
    static let build = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "Unknown"
    static let fullVersion = "\(marketing) (\(build))"
    
    static func isNewerVersion(_ version: String) -> Bool {
        return marketing.compare(version, options: .numeric) == .orderedAscending
    }
}

Android Play Store Sürümleme

kotlin
// build.gradle.kts
android {
    defaultConfig {
        versionCode = 23
        versionName = "2.1.3"
    }
}

// Sürüm yönetimi
object AppVersion {
    val versionName: String
        get() = BuildConfig.VERSION_NAME
    
    val versionCode: Int
        get() = BuildConfig.VERSION_CODE
    
    fun isNewerVersion(version: String): Boolean {
        return versionName.compareTo(version, ignoreCase = true) < 0
    }
}

Sürüm Pipeline

yaml
# GitHub Actions - Release Pipeline
name: Release Pipeline
on:
  push:
    tags:
      - 'v*'

jobs:
  build-ios:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v3
      - name: Setup Xcode
        uses: maxim-lobanov/setup-xcode@v1
        with:
          xcode-version: latest-stable
      - name: Build iOS
        run: |
          xcodebuild -workspace App.xcworkspace \
                     -scheme App \
                     -configuration Release \
                     -archivePath App.xcarchive \
                     archive
      - name: Export IPA
        run: |
          xcodebuild -exportArchive \
                     -archivePath App.xcarchive \
                     -exportPath ./build \
                     -exportOptionsPlist ExportOptions.plist
      - name: Upload to App Store
        uses: apple-actions/upload-testflight-build@v1
        with:
          app-path: './build/App.ipa'
          issuer-id: ${{ secrets.APPSTORE_ISSUER_ID }}
          api-key-id: ${{ secrets.APPSTORE_API_KEY_ID }}
          api-private-key: ${{ secrets.APPSTORE_API_PRIVATE_KEY }}
  build-android:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Setup Java
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'
      - name: Build Android
        run: ./gradlew assembleRelease
      - name: Sign APK
        uses: r0adkll/sign-android-release@v1
        with:
          releaseDirectory: app/build/outputs/apk/release
          signingKeyBase64: ${{ secrets.SIGNING_KEY }}
          alias: ${{ secrets.ALIAS }}
          keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
          keyPassword: ${{ secrets.KEY_PASSWORD }}
      - name: Upload to Play Store
        uses: r0adkll/upload-google-play@v1
        with:
          serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
          packageName: com.example.app
          releaseFiles: app/build/outputs/apk/release/app-release-signed.apk
          track: production

Kademeli Dağıtım

Kademeli Yayın Stratejisi

swift
// iOS Kademeli Yayın
class ReleaseManager {
    func checkRolloutEligibility() -> Bool {
        let userId = UserDefaults.standard.string(forKey: "user_id") ?? ""
        let userHash = userId.sha256()
        let rolloutPercentage = RemoteConfig.shared.getInt("rollout_percentage", defaultValue: 0)
        let userBucket = Int(userHash.prefix(8), radix: 16) ?? 0
        return (userBucket % 100) < rolloutPercentage
    }
    func shouldShowNewFeature() -> Bool {
        return checkRolloutEligibility() && AppVersion.isNewerVersion("2.1.0")
    }
}
extension String {
    func sha256() -> String {
        let data = Data(self.utf8)
        let hash = SHA256.hash(data: data)
        return hash.compactMap { String(format: "%02x", $0) }.joined()
    }
}

A/B Test Entegrasyonu

kotlin
class ReleaseExperimentManager {
    private val remoteConfig = Firebase.remoteConfig
    fun isUserInExperiment(experimentName: String): Boolean {
        val experimentConfig = remoteConfig.getString("experiment_$experimentName")
        val config = JSONObject(experimentConfig)
        val enabled = config.getBoolean("enabled")
        if (!enabled) return false
        val percentage = config.getInt("percentage")
        val userId = getUserId()
        return getUserBucket(userId) < percentage
    }
    private fun getUserBucket(userId: String): Int {
        val hash = userId.toByteArray().fold(0) { acc, byte -> 31 * acc + byte.toInt() }
        return kotlin.math.abs(hash % 100)
    }
    fun trackExperimentExposure(experimentName: String, variant: String) {
        FirebaseAnalytics.getInstance(context).logEvent("experiment_exposure") {
            param("experiment_name", experimentName)
            param("variant", variant)
            param("user_id", getUserId())
        }
    }
}

Modern sürüm yönetimi, otomasyon, kademeli dağıtım ve analitik entegrasyonu ile güvenli ve esnek mobil uygulama yayınları sağlar.

Eren Demir tarafından oluşturulmuştur.