Tüm Yazılar
KategorimacOS
Okuma Süresi
22 dk
Yayın Tarihi
...
Kelime Sayısı
1.431kelime

Kahveni hazırla - bu içerikli bir makale!

Mac uygulamaları geliştirme rehberi. SwiftUI + AppKit, menu bar apps, multi-window, document-based apps ve Catalyst.

macOS Development: SwiftUI ile Desktop Uygulamaları

Mac uygulamaları, mobil dünyadan farklı bir evren. Çoklu pencereler, menu bar, keyboard shortcuts, drag & drop, file system erişimi... iOS'ta alışık olmadığın birçok kavram var. Ama SwiftUI sayesinde, aynı dili kullanarak masaüstü uygulamalar geliştirebilirsin.

İçindekiler

  1. macOS vs iOS: Temel Farklar
  2. İlk macOS Uygulaması
  3. Multi-Window Desteği
  4. Menu Bar ve Toolbar
  5. Document-Based Apps
  6. AppKit Entegrasyonu
  7. Menu Bar Only Apps
  8. Mac Catalyst: iOS'tan macOS'a
  9. Production Best Practices

macOS vs iOS: Temel Farklar {#farklar}

Özellik
iOS
macOS
**Pencere**
Tek
Çoklu
**Navigation**
Push/modal
Sidebar + split view
**Input**
Touch
Mouse + keyboard
**File system**
Sandbox
Genişletilmiş erişim
**Menu**
Yok
Menu bar zorunlu
**Min boyut**
Sabit
Kullanıcı ayarlar
**Dock**
Yok
App icon + badge

Dış Kaynaklar:


İlk macOS Uygulaması {#ilk-uygulama}

swift
1@main
2struct MyMacApp: App {
3 var body: some Scene {
4 WindowGroup {
5 ContentView()
6 }
7 .windowStyle(.titleBar)
8 .windowToolbarStyle(.unified(showsTitle: true))
9 .defaultSize(width: 900, height: 600)
10 .commands {
11 // Custom menu komutları
12 CommandGroup(replacing: .newItem) {
13 Button("Yeni Proje") { createProject() }
14 .keyboardShortcut("n")
15 }
16 }
17 
18 // Settings penceresi
19 Settings {
20 SettingsView()
21 }
22 }
23}
24 
25struct ContentView: View {
26 @State private var selectedItem: SidebarItem?
27 
28 var body: some View {
29 NavigationSplitView {
30 // Sidebar - macOS'un kalbi
31 List(selection: $selectedItem) {
32 Section("Projeler") {
33 ForEach(projects) { project in
34 Label(project.name, systemImage: "folder")
35 .tag(project as SidebarItem?)
36 }
37 }
38 Section("Etiketler") {
39 ForEach(tags) { tag in
40 Label(tag.name, systemImage: "tag")
41 .tag(tag as SidebarItem?)
42 }
43 }
44 }
45 .listStyle(.sidebar)
46 .frame(minWidth: 200)
47 } detail: {
48 if let selectedItem {
49 DetailView(item: selectedItem)
50 } else {
51 ContentUnavailableView(
52 "Bir öğe seçin",
53 systemImage: "sidebar.left",
54 description: Text("Sol panelden bir proje veya etiket seçin")
55 )
56 }
57 }
58 .toolbar {
59 ToolbarItem(placement: .primaryAction) {
60 Button(action: {}) {
61 Label("Ekle", systemImage: "plus")
62 }
63 }
64 }
65 }
66}

Multi-Window Desteği {#multi-window}

swift
1@main
2struct MultiWindowApp: App {
3 var body: some Scene {
4 // Ana pencere
5 WindowGroup {
6 MainView()
7 }
8 
9 // Ayrı pencere türü
10 WindowGroup("Detay", id: "detail", for: UUID.self) { $itemId in
11 if let itemId {
12 DetailWindow(itemId: itemId)
13 }
14 }
15 .defaultSize(width: 500, height: 400)
16 .defaultPosition(.trailing)
17 
18 // Utility pencere
19 Window("Inspector", id: "inspector") {
20 InspectorView()
21 }
22 .defaultSize(width: 300, height: 600)
23 .windowStyle(.titleBar)
24 .windowResizability(.contentSize)
25 }
26}
27 
28// Pencere açma
29struct MainView: View {
30 @Environment(\.openWindow) var openWindow
31 
32 var body: some View {
33 Button("Detay Aç") {
34 openWindow(id: "detail", value: selectedItem.id)
35 }
36 }
37}

swift
1// Custom Menu Commands
2struct AppCommands: Commands {
3 @FocusedBinding(\.selectedDocument) var document
4 
5 var body: some Commands {
6 // File menüsüne ekleme
7 CommandGroup(after: .newItem) {
8 Button("Şablondan Oluştur...") {
9 // Template picker aç
10 }
11 .keyboardShortcut("n", modifiers: [.command, .shift])
12 }
13 
14 // Custom menü
15 CommandMenu("Proje") {
16 Button("Derle") { }
17 .keyboardShortcut("b")
18 Button("Çalıştır") { }
19 .keyboardShortcut("r")
20 Divider()
21 Button("Temizle") { }
22 .keyboardShortcut("k", modifiers: [.command, .shift])
23 }
24 
25 // Toolbar
26 ToolbarCommands()
27 SidebarCommands()
28 }
29}
30 
31// Keyboard shortcuts
32struct EditorView: View {
33 var body: some View {
34 TextEditor(text: $content)
35 .keyboardShortcut(.defaultAction) // Enter = default action
36 .onKeyPress(.escape) {
37 dismiss()
38 return .handled
39 }
40 }
41}

swift
1@main
2struct MenuBarApp: App {
3 var body: some Scene {
4 // Sadece menu bar'da yaşayan app
5 MenuBarExtra("My Utility", systemImage: "bolt.fill") {
6 VStack(spacing: 12) {
7 Text("CPU: 45%")
8 Text("RAM: 8.2 GB")
9 Text("Disk: 120 GB free")
10 
11 Divider()
12 
13 Button("Detaylar...") { openDetailWindow() }
14 Button("Çıkış") { NSApplication.shared.terminate(nil) }
15 .keyboardShortcut("q")
16 }
17 .padding()
18 }
19 .menuBarExtraStyle(.window) // Popup pencere
20 }
21}

Document-Based Apps {#document-based}

macOS'un en güçlü özelliklerinden biri document-based uygulama desteği. SwiftUI ile dosya tabanlı uygulamalar oluşturmak oldukça kolay:

swift
1// 1. Document tipi tanımla
2struct TextDocument: FileDocument {
3 static var readableContentTypes: [UTType] { [.plainText] }
4 
5 var text: String
6 
7 init(text: String = "") {
8 self.text = text
9 }
10 
11 init(configuration: ReadConfiguration) throws {
12 guard let data = configuration.file.regularFileContents,
13 let text = String(data: data, encoding: .utf8)
14 else {
15 throw CocoaError(.fileReadCorruptFile)
16 }
17 self.text = text
18 }
19 
20 func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
21 let data = text.data(using: .utf8)!
22 return FileWrapper(regularFileWithContents: data)
23 }
24}
25 
26// 2. Document-based App scene
27@main
28struct TextEditorApp: App {
29 var body: some Scene {
30 DocumentGroup(newDocument: TextDocument()) { file in
31 TextEditorView(document: file.$document)
32 }
33 .commands {
34 TextFormattingCommands()
35 }
36 }
37}
38 
39// 3. Editor view
40struct TextEditorView: View {
41 @Binding var document: TextDocument
42 @State private var fontSize: CGFloat = 14
43 @FocusState private var isEditorFocused: Bool
44 
45 var body: some View {
46 TextEditor(text: $document.text)
47 .font(.system(size: fontSize, design: .monospaced))
48 .focused($isEditorFocused)
49 .toolbar {
50 ToolbarItem {
51 Stepper("Font: \(Int(fontSize))pt",
52 value: $fontSize, in: 8...72)
53 }
54 }
55 .navigationTitle(document.text.prefix(30) + "...")
56 .onAppear { isEditorFocused = true }
57 }
58}

Document App Türleri

Tür
Kullanım
SwiftUI API
**FileDocument**
Value-type document (struct)
`DocumentGroup(newDocument:)`
**ReferenceFileDocument**
Class-based document (undo desteği)
`DocumentGroup(newDocument:) { }`
**Custom UTType**
Kendi dosya formatın
`UTType` extension ile tanımla

Drag & Drop Desteği

macOS uygulamalarında drag & drop kritik bir etkileşim pattern'idir:

swift
1struct FileDropView: View {
2 @State private var droppedFiles: [URL] = []
3 @State private var isTargeted = false
4 
5 var body: some View {
6 VStack {
7 ForEach(droppedFiles, id: \.self) { url in
8 Label(url.lastPathComponent, systemImage: "doc")
9 }
10 }
11 .frame(maxWidth: .infinity, maxHeight: .infinity)
12 .background(isTargeted ? Color.blue.opacity(0.2) : Color.clear)
13 .dropDestination(for: URL.self) { urls, _ in
14 droppedFiles.append(contentsOf: urls)
15 return true
16 } isTargeted: { targeted in
17 isTargeted = targeted
18 }
19 }
20}

Mac Catalyst: iOS'tan macOS'a {#catalyst}

Mevcut iPad uygulamanı Mac'e taşımanın en hızlı yolu Mac Catalyst:

swift
1// Catalyst-specific optimizasyonlar
2#if targetEnvironment(macCatalyst)
3extension AppDelegate {
4 override func buildMenu(with builder: UIMenuBuilder) {
5 // macOS menü çubuğuna özel komutlar ekle
6 let prefsCommand = UIKeyCommand(
7 title: "Tercihler...",
8 action: #selector(showPreferences),
9 input: ",",
10 modifierFlags: .command
11 )
12 let prefsMenu = UIMenu(title: "", options: .displayInline, children: [prefsCommand])
13 builder.insertChild(prefsMenu, atStartOfMenu: .application)
14 }
15}
16#endif
Yaklaşım
Avantaj
Dezavantaj
**Native SwiftUI**
Tam platform desteği
Ayrı geliştirme gerektirir
**Mac Catalyst**
Hızlı geçiş, kod paylaşımı
AppKit erişimi kısıtlı
**Designed for iPad**
Sıfır effort
macOS deneyimi zayıf

AppKit Entegrasyonu {#appkit}

SwiftUI'da olmayan macOS feature'ları için AppKit kullanılır:

swift
1// NSViewRepresentable ile AppKit view'ı SwiftUI'da kullan
2struct ColorWellView: NSViewRepresentable {
3 @Binding var selectedColor: Color
4 
5 func makeNSView(context: Context) -> NSColorWell {
6 let colorWell = NSColorWell()
7 colorWell.action = #selector(Coordinator.colorChanged(_:))
8 colorWell.target = context.coordinator
9 return colorWell
10 }
11 
12 func updateNSView(_ nsView: NSColorWell, context: Context) {
13 nsView.color = NSColor(selectedColor)
14 }
15 
16 func makeCoordinator() -> Coordinator {
17 Coordinator(self)
18 }
19 
20 class Coordinator: NSObject {
21 var parent: ColorWellView
22 
23 init(_ parent: ColorWellView) { self.parent = parent }
24 
25 @objc func colorChanged(_ sender: NSColorWell) {
26 parent.selectedColor = Color(nsColor: sender.color)
27 }
28 }
29}

Production Best Practices {#best-practices}

🔑 Çıkarımlar

  1. NavigationSplitView macOS'un temel navigation pattern'i
  2. Keyboard shortcuts zorunlu - Mac kullanıcıları klavyecidir
  3. Multi-window desteği düşün - özellikle document-based app'lerde
  4. Menu bar komutları anlamlı olsun
  5. Minimum pencere boyutu ayarla - küçülünce bozulmasın
  6. Dark/Light mode doğal destekle - macOS'ta çok önemli

Easter Egg

Gizli bir bilgi buldun!

Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?

Okuyucu Ödülü

Tebrikler! Bu yazıyı sonuna kadar okuduğun için sana özel bir hediyem var:

ALTIN İPUCU

Bu yazının en değerli bilgisi

Bu ipucu, yazının en önemli çıkarımını içeriyor.

Etiketler

#macos#desktop#swiftui#appkit#mac-catalyst#multi-window#tutorial
Muhittin Çamdalı

Muhittin Çamdalı

Senior iOS Developer

12+ yıllık deneyime sahip iOS Developer. Swift, SwiftUI ve modern iOS mimarileri konusunda uzman. Apple platformlarında performanslı ve kullanıcı dostu uygulamalar geliştiriyorum.

iOS Geliştirme Haberleri

Haftalık Swift tips, SwiftUI tricks ve iOS best practices. Spam yok, sadece değerli içerik.

Gizliliğinize saygı duyuyoruz. İstediğiniz zaman abonelikten çıkabilirsiniz.

Paylaş

Bunu da begenebilirsiniz