In this tutorial, we’ll create an iOS app that allows users to search for photos using the Pixabay API, display the results in a photo gallery, and view individual photos in detail. We’ll use SwiftUI for the user interface, Alamofire for networking, and integrate with the Pixabay API for fetching images.
Project Setup
First, create a new SwiftUI project in Xcode. Then, we’ll use CocoaPods to manage our dependencies. Create a Podfile in your project directory with the following content:
platform :ios, '14.0' use_frameworks! target 'PhotoExplorer' do pod 'Alamofire' pod 'SDWebImageSwiftUI' end
Run pod install
in your terminal, then open the .xcworkspace
file.
The Photo Model
Let’s start by defining our Photo model:
import Foundation struct Photo: Identifiable, Codable { let id: Int let pageURL: String let previewURL: String let largeImageURL: String let user: String let tags: String } struct PixabayResponse: Codable { let total: Int let totalHits: Int let hits: [Photo] }
This model matches the JSON structure returned by the Pixabay API.
Networking Service
Next, let’s create a networking service using Alamofire:
import Foundation import Alamofire class PixabayService { static let shared = PixabayService() private init() {} private let baseURL = "https://pixabay.com/api/" private let apiKey = "YOUR_PIXABAY_API_KEY" func searchPhotos(query: String, completion: @escaping (Result<[Photo], Error>) -> Void) { let parameters: [String: Any] = [ "key": apiKey, "q": query, "image_type": "photo", "per_page": 50 ] AF.request(baseURL, parameters: parameters).responseDecodable(of: PixabayResponse.self) { response in switch response.result { case .success(let pixabayResponse): completion(.success(pixabayResponse.hits)) case .failure(let error): completion(.failure(error)) } } } }
Don’t forget to replace "YOUR_PIXABAY_API_KEY"
with your actual Pixabay API key.
Main View
Now, let’s create our main view with a search bar and photo grid:
import SwiftUI import SDWebImageSwiftUI struct ContentView: View { @State private var searchText = "famous quotes" @State private var photos: [Photo] = [] let columns = [GridItem(.adaptive(minimum: 100))] var body: some View { NavigationView { VStack { SearchBar(text: $searchText, onSearchButtonClicked: fetchPhotos) ScrollView { LazyVGrid(columns: columns, spacing: 20) { ForEach(photos) { photo in NavigationLink(destination: PhotoDetailView(photo: photo)) { PhotoThumbnail(imageURL: photo.previewURL) } } } .padding() } } .navigationTitle("Pixabay Gallery") } .onAppear(perform: fetchPhotos) } private func fetchPhotos() { PixabayService.shared.searchPhotos(query: searchText) { result in switch result { case .success(let fetchedPhotos): DispatchQueue.main.async { self.photos = fetchedPhotos } case .failure(let error): print("Error fetching photos: \(error)") } } } } struct SearchBar: View { @Binding var text: String var onSearchButtonClicked: () -> Void var body: some View { HStack { TextField("Search photos", text: $text, onCommit: onSearchButtonClicked) .textFieldStyle(RoundedBorderTextFieldStyle()) Button(action: onSearchButtonClicked) { Image(systemName: "magnifyingglass") } } .padding() } } struct PhotoThumbnail: View { let imageURL: String var body: some View { WebImage(url: URL(string: imageURL)) .resizable() .indicator(.activity) .transition(.fade(duration: 0.5)) .scaledToFill() .frame(width: 100, height: 100) .clipShape(RoundedRectangle(cornerRadius: 10)) } }
Detail View
Finally, let’s create our detail view:
import SwiftUI import SDWebImageSwiftUI struct PhotoDetailView: View { let photo: Photo @State private var scale: CGFloat = 1.0 var body: some View { VStack { ZoomableImageView(imageURL: photo.largeImageURL, scale: $scale) } .frame(maxWidth: .infinity, maxHeight: .infinity) .navigationTitle("Photo Details") .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button("Reset Zoom") { scale = 1.0 } } } } } struct ZoomableImageView: View { let imageURL: String @Binding var scale: CGFloat var body: some View { WebImage(url: URL(string: imageURL)) .resizable() .indicator(.activity) .transition(.fade(duration: 0.5)) .scaledToFit() .scaleEffect(scale) .gesture( MagnificationGesture() .onChanged { value in scale = value.magnitude } ) .onTapGesture(count: 2) { if scale > 1 { scale = 1 } else { scale = 2 } } } }
How It Works
- When the app launches, it automatically searches for “famous quotes” using the Pixabay API.
- The search results are displayed in a grid of thumbnails.
- Users can enter a new search term and tap the search button to fetch new photos.
- Tapping on a photo thumbnail navigates to the detail view.
- In the detail view, users can:
- See the full-size photo
- Zoom in/out using pinch gestures or double-tap
This app demonstrates several important iOS development concepts:
- Using SwiftUI for building user interfaces
- Networking with Alamofire
- Integrating with a third-party API (Pixabay)
- Displaying and caching images with SDWebImageSwiftUI
- Implementing zoom functionality
- Navigation and data passing between views
By building this app, you’ve created a functional and interactive photo gallery that fetches real-world data from the internet. You can further enhance this app by adding features like pagination, saving favourite photos, or implementing more advanced search filters.