≡ Menu

Realizzare un’app di citazioni a caso in Swift utilizzando SwiftUI e Combine

In questo articolo, esploreremo come costruire un’applicazione mobile iOS semplice che visualizza una citazione casuale da un elenco di citazioni memorizzate in un file di testo online. Questo esercizio ci permetterà di imparare a utilizzare alcune importanti tecnologie Swift, tra cui SwiftUI per l’interfaccia utente e Combine per la gestione degli eventi asincroni.

Iniziamo descrivendo l’architettura generale del nostro progetto. L’applicazione sarà composta da una singola schermata che visualizza una citazione casuale e offre un pulsante per caricare una nuova citazione. Le citazioni saranno memorizzate in un file di testo online, con una particolare formattazione per separare ogni citazione.

Partiamo da una semplice applicazione SwiftUI. Creiamo una nuova classe Cancellables per tenere traccia dei nostri processi asincroni. Utilizzeremo la libreria Combine di Swift per gestire i processi asincroni e i cambiamenti di stato.

import SwiftUI
import Combine

class Cancellables: ObservableObject {
    var set = Set<AnyCancellable>()
}

Successivamente, creiamo il nostro ContentView, che è il componente principale della nostra applicazione.

struct ContentView: View {
    @State private var text = "Loading..."
    private let url = URL(string: "https://www.raucci.net/frasi.txt")!
    @StateObject private var cancellables = Cancellables()
    @State private var rotation = 0.0

Abbiamo definito alcune variabili di stato: text per la citazione attuale, url per l’URL del nostro file di citazioni, cancellables per conservare i processi asincroni e rotation per gestire l’animazione dell’immagine.

A seguire, definiamo la struttura visiva del nostro ContentView.

    var body: some View {
        NavigationView {
            VStack {
                Image("yourImageName") 
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .scaleEffect(0.8) 
                    .rotationEffect(.degrees(rotation))
                    .onTapGesture {
                        fetchQuotes()
                        withAnimation(.easeInOut(duration: 1.0)) {
                            self.rotation += 360
                        }
                    }
                Text(text)
                    .font(.custom("jgs_Font", size: 38)) 
                    .padding() 
                    .frame(maxWidth: .infinity, maxHeight: .infinity) 
            }
            .navigationTitle("fortune...")
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    Button(action: copyQuote) {
                        Image(systemName: "doc.on.doc")
                    }
                }
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button(action: fetchQuotes) {
                        Image(systemName: "arrow.clockwise")
                    }
                }
            }
        }
        .onAppear {
            fetchQuotes()
        }
    }

Qui abbiamo definito una NavigationView con una VStack che contiene un’immagine e un Text. L’immagine è configurata per ridimensionarsi, mantenere le proporzioni, ridursi all’80% della sua dimensione originale e ruotare in base alla variabile di stato rotation. Il Text visualizza la citazione attuale e ha un font personalizzato, del padding e viene centrato nella vista. Abbiamo anche una barra di navigazione con due pulsanti: uno per copiare la citazione attuale e l’altro per caricare una nuova citazione.

La funzione fetchQuotes viene chiamata quando l’utente tocca l’immagine o il pulsante di caricamento e quando la vista appare.

    private func copyQuote() {
        UIPasteboard.general.string = text
    }

La funzione copyQuote copia la citazione attuale negli appunti del dispositivo.

    private func fetchQuotes() {
        URLSession.shared.dataTaskPublisher(for: url)
            .map { data, _ in String(data: data, encoding: .utf8) }
            .receive(on: DispatchQueue.main)
            .sink(receiveCompletion: { completion in
                if case .failure(let err) = completion {
                    self.text = "Failed to load: \(err)"
                }
            }, receiveValue: { text in self.processText(text) })
            .store(in: &cancellables.set)
    }

fetchQuotes avvia un processo asincrono per caricare il contenuto del nostro file di citazioni. Una volta che il contenuto è stato caricato, viene invocata la funzione processText.

    private func processText(_ text: String?) {
        guard let text

 = text else {
            self.text = "Failed to load"
            return
        }

        let trimmedText = text.replacingOccurrences(of: "\n", with: " ")
        let components = trimmedText.components(separatedBy: "#-")
        var phrases: [String] = []

        for component in components.dropFirst() {
            if let endRange = component.range(of: "-#") {
                let phrase = String(component[..<endRange.lowerBound]).trimmingCharacters(in: .whitespacesAndNewlines)
                if !phrase.isEmpty {
                    phrases.append(phrase)
                }
            }
        }

        if phrases.isEmpty {
            self.text = "No phrases found"
        } else {
            self.text = phrases.randomElement() ?? "No phrases found"
        }
    }
}

La funzione processText processa il contenuto del file di citazioni, estrae le singole citazioni e seleziona una citazione casuale.

{ 0 comments… add one }

Rispondi