Skip to main content

52 posts tagged with "Swift"

View All Tags

INTRODUCTION TO USING TIPKIT IN SWIFTUI APPS

Published: · Last updated: · 4 min read
Don Peter
Cofounder and CTO, Appxiom

TipKit is a new beta framework introduced in iOS 17 that helps developers present tips and instructions to users in their apps. With TipKit, you can easily create and manage tips, set rules for when and how they are displayed.

In this blog post, we will walk you through the steps of getting started with TipKit in your SwiftUI apps.

Steps to using TipKit

1. Creating a Tip

To use TipKit, you first need to import the TipKit framework into your project, along with the SwiftUI framework. Then, you can create a new Tip object and specify its content, title, and other properties.

import SwiftUI
import TipKit


struct FavoriteBookTip: Tip {

var title: Text {
Text("Add as Favorite book")
}


var message: Text? {
Text("Click on the fav icon to add the book to favourites")
}

}

For a tip to be valid, it is mandatory to set its title.

2. Adding Rules

Next step is to add rules that must be met in order for the tip to be displayed. The rules property is an array of Predicate objects, each of which specifies a condition that must be met.

import SwiftUI
import TipKit


struct FavoriteBookTip: Tip {

var title: Text {
Text("Add as Favorite book")
}


var message: Text? {
Text("Click on the fav icon to add the book to favourites")
}

var rules: Predicate<RuleInput...> {
#Rule(Self.$isLoggedIn) { $0 == true }
}
}

In this case, the only rule is that the isLoggedIn property must be equal to true. This means that the tip will only be displayed if the user is logged in.

The #Rule syntax is used to create rules. In this case, the #Rule tag is used to create a rule that is based on the Self.$isLoggedIn property. The Self keyword refers to the current view, and the $isLoggedIn property is a property that gets the current user's login status.

3. Adding Tip to SwiftUI view

import SwiftUI
import TipKit


struct FavoriteBookTip: Tip {


var title: Text {
Text("Add as Favorite book")
}


var message: Text? {
Text("Click on the fav icon to add the book to favourites")
}

var rules: Predicate<RuleInput...> {
#Rule(Self.$isLoggedIn) { $0 == true }
}
}


@main
struct BookTips: App {
var bookTip = FavoriteBookTip()


var body: some Scene {
WindowGroup {
VStack {
TipView(bookTip, arrowEdge: .bottom)

Image(systemName: "fav_icon")
.imageScale(.large)
Spacer()
}
}
}
}

TipView is a user interface element that represents an inline tip that is provided by TipKit. It displays a tip with an arrow that points to the bottom of the screen. The arrowEdge parameter specifies the edge of the screen where the arrow should point.

In this case, the arrowEdge parameter is set to .bottom, so the arrow will point to the bottom of the screen. TipView takes FavoriteBookTip object as its argument and displays the tip with an arrow that points to the bottom of the screen.

4. Configuring Tip using TipsCenter

Once you have created a tip, you can configure TipsCenter.

TipsCenter is a key part of TipKit that provides several essential features. It allows tips and their associated events to persist between app launches, making it easier to test tips. A default shared instance of TipsCenter is provided, which is what I have added here.

TipsCenter.shared.configure(
displayFrequency: .daily
)

The displayFrequency property specifies how often the tip should be displayed. In this case, the tip will be displayed once per day.

Once you have created your tip and configured TipsCenter, you can display it using the following code when testing.

TipsCenter.shared.showTips([bookTip])

Use-cases of TipKit

Here are a few examples of how you can use TipKit in your SwiftUI or UIKit based apps:

  • Display a tip when a user opens your app for the first time.

  • Show a tip when a user performs a specific action, such as taking a photo or adding a contact.

  • Give users tips on how to use your app's features.

  • Provide instructions on how to troubleshoot common problems.

Conclusion

TipKit is a powerful new framework that can help you improve the user experience of your SwiftUI or UIKit based apps. By using TipKit, you can easily create and manage tips, set rules for when and how they are displayed, and track their effectiveness.

To make sure your tips are effective, keep them short, instructional, and actionable.

COMBINE: A DECLARATIVE API FOR ASYNCHRONOUS DATA PROCESSING IN SWIFT

Published: · Last updated: · 4 min read
Don Peter
Cofounder and CTO, Appxiom

Combine is a framework for Swift introduced by Apple in 2019 that provides a declarative API. This makes it ideal for working with asynchronous data, such as network requests and user input. Combine is also a powerful tool for building reactive user interfaces.

In this blog post, we will take a look at the basics of Combine, including publishers, subscribers, and operators. We will also see how Combine can be used to build asynchronous applications and reactive user interfaces.

What is Combine?

Combine is a reactive programming framework that provides a declarative API for processing values over time. This means that you can describe the desired behaviour of your code without having to worry about the details of how it will be implemented.

Combine is based on the following concepts:

  • Publishers: Publishers emit values over time. They can be anything from network requests to user input.

  • Subscribers: Subscribers receive values from publishers. They can do things like map values, filter values, and perform other operations.

  • Operators: Operators are functions that combine publishers and subscribers. They can be used to perform common tasks, such as combining multiple publishers, filtering values, and retrying failed requests.

Using Combine to Build Asynchronous Applications in Swift

Combine is ideal for building asynchronous applications. This is because it provides a way to handle asynchronous events in a declarative way. For example, you can use Combine to make a network request and then subscribe to the response. The subscriber can then handle the response, such as mapping it to a model or displaying it in a user interface.

Here is an example of how to use Combine to make a network request:

let publisher = URLSession.shared.dataTaskPublisher(for: URL(string: "https://api.myhost.com")!)

publisher.subscribe(on: RunLoop.main) { data, _, error in
if let data = data {
let json = try JSONDecoder().decode(MyJSONModel.self, from: data)
// Do something with the model
} else if let error = error {
// Handle the error
}
}

This code creates a publisher that emits the response data from the network request. The subscriber then handles the response data, either mapping it to a model or displaying it in a user interface.

Using Combine to Build Reactive User Interfaces

Combine can also be used to build reactive user interfaces. This is because it provides a way to update user interfaces in response to changes in data. For example, you can use Combine to subscribe to a publisher that emits the current user location. The subscriber can then update the user interface to display the user's location.

Here is an example of how to use Combine to update a user interface with the current user location:

let publisher = locationManager.publisher(for: .location)

publisher.subscribe(on: RunLoop.main) { location in
// Update the user interface with the new location
}

This code creates a publisher that emits the current user location. The subscriber then updates the user interface to display the user's location.

Using custom Combine implementation

Let us take a look at using PassthroughSubject to implement asynchronous declarative API.

A PassthroughSubject is a type of publisher in Combine that emits any value that is sent to it. It does not have an initial value or a buffer of the most recently-published element. This makes it ideal for use in situations where you need to react to changes in data as they happen.

import Combine

let subject = PassthroughSubject<String, Never>()

subject.sink { string in
print(string)
}

subject.send("Hello, world!")
subject.send("This is a second message")

Here, the first line imports the Combine framework. This is needed to use the PassthroughSubject and sink operators.

The second line creates a PassthroughSubject publisher. This publisher will emit any string that is sent to it.

The third line attaches a sink subscriber to the PassthroughSubject publisher. The sink subscriber will print each string that is emitted by the publisher to the console.

The fourth and fifth lines send two strings to the PassthroughSubject publisher. These strings will be printed to the console by the sink subscriber.

Conclusion

Combine is a framework that provides a declarative API for processing values over time. This makes it ideal for working with asynchronous data and building reactive user interfaces. If you are new to Combine, I encourage you to check out the official documentation and tutorials.

I hope this blog post has given you a basic understanding of Combine. If you have any questions, please feel free to leave a comment below.

INTRODUCTION TO STATE MANAGEMENT IN SWIFTUI: @STATE, @STATEOBJECT AND @OBSERVEDOBJECT

Published: · Last updated: · 6 min read
Don Peter
Cofounder and CTO, Appxiom

SwiftUI is a powerful framework for building user interfaces for Apple devices. However, one of the challenges of using SwiftUI is managing state. State is the data that changes over time in your app, such as the current user's location or the contents of a shopping cart.

Using @State property wrapper in SwiftUI

There are a few different ways to manage state in SwiftUI. The simplest way is to use the @State property wrapper. The @State property wrapper allows you to store a value that can be changed within a view. When the value changes, SwiftUI will automatically update the view.

For example, let's say we have a view that displays a counter. We can use the @State property wrapper to store the current value of the counter. When the user taps a button, we can increment the counter value and then update the view.

struct CounterView: View {
@State private var counter = 0

var body: some View {
Button("Increment") {
counter += 1
}
Text("\(counter)")
}
}

The @State property wrapper is a great way to manage simple state in SwiftUI.

However, some of the limitations of using @State are,

  • @State properties can only be used in structs. This means that you can't use @State properties in classes or enums.

  • @State properties can't be used to store complex objects. This means that you can't store objects that contain functions, closures, or other complex types in a @State property.

  • @State properties can't be changed from outside the view. This means that you can't change the value of a @State property from another view or from code that isn't part of the view hierarchy.

Using @StateObject and @ObservedObject

The code below shows how to use the @StateObject and @ObservedObject property wrappers to manage state in SwiftUI.

The GameProgress class is an ObservableObject class. This means that it conforms to the ObservableObject protocol, which allows it to be observed by other views. The points property in the GameProgress class is marked with the @Published property wrapper.

This means that any changes to the value of the points property will be automatically published to any views that are observing it.

The ButtonView struct is a view that observes the progress property. The progress property is marked with the @ObservedObject property wrapper, which tells SwiftUI that the view should observe the value of the property and update itself whenever the value changes. The ButtonView struct has a button that increments the value of the points property. When the button is tapped, the points property is incremented and the InnerView struct is updated to reflect the change.

The ContentView struct is the main view of the app. It has a progress property that is an instance of the GameProgress class. The progress property is marked with the @StateObject property wrapper, which tells SwiftUI that the property is owned by the ContentView view. The ContentView struct has a VStack that contains two views: a Text view that displays the current points, and an ButtonView view that allows the user to increment the points.

class GameProgress: ObservableObject {
@Published var points = 0
}

struct ButtonView: View {
@ObservedObject var progress: GameProgress

var body: some View {
Button("Increase Points") {
progress.points += 1
}
}
}

struct ContentView: View {
@StateObject var progress = GameProgress()

var body: some View {
VStack {
Text("Your points are \(progress.points)")
ButtonView(progress: progress)
}
}
}

Here are some key takeaways from this code:

  • The @StateObject property wrapper is used to create an object that can be observed by other views.

  • The @Published property wrapper is used to mark a property in an ObservableObject class as being observable.

  • The @ObservedObject property wrapper is used to observe a property in an ObservableObject class from another view.

  • When the value of a property that is marked with the @Published property wrapper changes, the views that are observing the property will be updated automatically.

This is a simple example of how to use the @StateObject and @ObservedObject property wrappers to manage state in SwiftUI. In a more complex app, the GameProgress class would likely be responsible for managing more than just the points. It might also be responsible for fetching data from a server or interacting with other parts of the app.

Using @EnvironmentObject

final class MyTheme: ObservableObject {
@Published var mainColor: Color = .purple
}

struct ThemeApp: App {
@StateObject var myTheme = MyTheme()

var body: some Scene {
WindowGroup {
ThemesListView()
.environmentObject(myTheme) // Make the theme available through the environment.
}
}
}

And the ThemesListView struct will be,

struct ThemesListView: View {

@EnvironmentObject var myTheme: Theme

Text("Text Title")
.backgroundColor(myTheme.mainColor)

}

The code is for a SwiftUI app that uses an environment object to share a theme between views. The theme object is a MyTheme class that conforms to the ObservableObject protocol. This means that the theme object can be observed by other views.

The ThemeApp struct is the main entry point for the app. It creates a myTheme property that is an instance of the MyTheme class. The myTheme property is marked with the @StateObject property wrapper, which means that it is owned by the ThemeApp struct.

The ThemeApp struct also has a body property that returns a WindowGroup. The WindowGroup contains an ThemesListView view. The ThemesListView view is a view that displays a list of themes.

The ThemesListView view uses the environmentObject modifier to access the myTheme property. This modifier tells SwiftUI to look for the myTheme property in the environment of the ThemesListView view. If the myTheme property is not found in the environment, then a new instance of the MyTheme class will be created.

The ThemesListView view uses the myTheme.mainColor property to set the color of the list items. This means that the color of the list items will be updated automatically whenever the mainColor property of the myTheme object changes.

Using an environment object is a simple and elegant solution. We only have to create the theme object once, and it will be available to all child views automatically. This makes our code easier to read and maintain.

Conclusion

In this blog post, we have explored three different ways to manage state in SwiftUI. We have seen how to use the @State property wrapper to manage simple state, how to use the @StateObject and @ObservedObject property wrappers to manage complex state, and how to use environment objects to share state between views.

The best approach to use will depend on the specific needs of your app.

ADVANTAGES OF STRUCTS IN SWIFT AND HOW TO USE THEM EFFECTIVELY

Published: · Last updated: · 4 min read
Appxiom Team
Mobile App Performance Experts

In Swift, structs are an essential feature of the language that allows developers to create custom data types to encapsulate related pieces of data and functionality. Unlike classes, structs are value types, meaning they are copied when passed around, which has numerous advantages.

In this blog, we'll explore the benefits of using structs in Swift and provide insights into how to use them effectively in your code.

Advantages of Using Structs

1. Value Semantics

One of the most significant advantages of using structs is their value semantics. When you create an instance of a struct and assign it to another variable or pass it as a parameter to a function, a complete copy of the struct is made. This behavior eliminates issues related to shared mutable state, making code more predictable and less prone to bugs.

struct Point {
var x: Int
var y: Int
}

var point1 = Point(x: 10, y: 20)
var point2 = point1 // Creates a copy of the struct
point2.x = 100 // Only modifies point2, leaving point1 unchanged

2. Performance and Memory Efficiency

Since structs are copied by value, they are stored directly where they are used, usually on the stack. This allocation strategy results in better memory management and performance compared to reference types (classes) that use heap storage. Structs are particularly useful for small, lightweight data types, which are prevalent in many applications.

3. Thread Safety

Due to their immutability and value semantics, structs are inherently thread-safe. Since they cannot be mutated once created, they eliminate the need for synchronization mechanisms like locks or serial dispatch queues in concurrent programming scenarios.

4. Swift Standard Library Foundation

Many essential Swift types, such as Int, Double, Bool, String, Array, and Dictionary, are implemented as structs in the Swift Standard Library. Leveraging structs enables you to build on top of these foundational types effectively.

5. Copy-on-Write Optimization

Swift's copy-on-write optimization further enhances the performance of structs. When a copy of a struct is made, the actual data is not duplicated immediately. Instead, both copies share the same data. The data is only duplicated when one of the copies is modified, ensuring efficient memory management.

Effective Usage of Structs

1. Model Data

Structs are ideal for modeling data, especially when dealing with simple objects with no need for inheritance or identity. For example, consider using structs to represent geometric shapes, user profiles, or configuration settings.

struct Circle {
var radius: Double
var center: Point
}

struct UserProfile {
var username: String
var email: String
var age: Int
}

2. Immutability

Consider making structs immutable whenever possible. Immutable structs prevent accidental modifications, leading to more robust and predictable code.

struct ImmutablePoint {
let x: Int
let y: Int
}

3. Small-sized Data Structures

As mentioned earlier, structs are great for small-sized data structures. For larger and more complex data structures, classes might be a more appropriate choice.

4. Use Extensions for Additional Functionality

To keep the primary purpose of a struct focused and maintain separation of concerns, use extensions to add extra functionality.

struct Point {
var x: Int
var y: Int
}

extension Point {
func distance(to otherPoint: Point) -> Double {
let xDist = Double(x - otherPoint.x)
let yDist = Double(y - otherPoint.y)
return (xDist * xDist + yDist * yDist).squareRoot()
}
}

5. Use Mutating Methods Sparingly

If you need to modify a struct, you must declare the method as mutating. However, try to limit the number of mutating methods and prefer immutability whenever possible.

Conclusion

Swift structs offer numerous advantages, including value semantics, performance, thread safety, and easy integration with the Swift Standard Library. By using structs effectively, you can write more robust, predictable, and efficient code. Remember to choose structs when modeling small-sized data and prefer immutability for improved code safety. Swift's powerful language features, combined with the advantages of structs, make it a great choice for developing applications across various domains.

Remember to practice and experiment with structs in your code to gain a deeper understanding of their advantages and to leverage their capabilities effectively.

Happy coding!

CREATING ACCESSIBLE IOS APPS: A GUIDE TO INCLUSIVITY AND ACCESSIBILITY IN APP DEVELOPMENT

Published: · Last updated: · 4 min read
Appxiom Team
Mobile App Performance Experts

In today's diverse and inclusive world, it's essential to design and develop apps that are accessible to individuals with disabilities.

In this blog, we'll explore how to create iOS apps that prioritize accessibility, ensuring that every user can enjoy and navigate through your app seamlessly. We'll cover important aspects such as accessibility APIs, VoiceOver support, dynamic type, accessible layout, and assistive technologies using Swift and SwiftUI code examples.

1. Understanding Accessibility in iOS Apps

Accessibility is about making your app usable and navigable by people with various disabilities, such as visual impairments, hearing impairments, motor skill limitations, and more. By following accessibility best practices, you can enhance your app's user experience and make it inclusive to a wider audience.

2. Setting Up Accessibility in Your Project

In Xcode, when you create a new project, you'll find an option to enable accessibility. Ensure that this option is selected from the beginning to set up the project with accessibility support.

3. Accessibility APIs

iOS provides a range of Accessibility APIs that developers can use to make their apps accessible. Some of the most commonly used APIs include:

  • UIAccessibility: This protocol helps to identify and describe the elements of your UI to assistive technologies. Conform to this protocol in custom views to provide relevant accessibility information.

  • UIAccessibilityElement: Implement this class to create custom accessibility elements within your views. It allows you to provide custom accessibility traits, labels, and hints.

4. VoiceOver Support

VoiceOver is a built-in screen reader on iOS devices that reads the content of the screen aloud, making it accessible to users with visual impairments. Ensure your app works seamlessly with VoiceOver by:

  • Providing meaningful accessibility labels: Use the accessibilityLabel property on UI elements to give descriptive labels to buttons, images, and other interactive elements.

  • Adding accessibility hints: Use the accessibilityHint property to provide additional context or instructions for VoiceOver users.

Example:

import SwiftUI

struct AccessibleButton: View {
var body: some View {
Button(action: {
// Your button action here
}) {
Text("Tap me")
.accessibilityLabel("A button that does something")
.accessibilityHint("Double-tap to activate")
}
}
}

5. Dynamic Type

iOS supports Dynamic Type, which allows users to adjust the system font size according to their preferences. To ensure your app is compatible with Dynamic Type, use system fonts and prefer relative font weights. Avoid hardcoding font sizes.

Example:

swiftCopy code
import SwiftUI

struct AccessibleText: View {
var body: some View {
Text("Hello, World!")
.font(.title)
.fontWeight(.bold)
.multilineTextAlignment(.center)
.lineLimit(0)
.padding()
.minimumScaleFactor(0.5) // Allows text to scale down for smaller fonts
.allowsTightening(true) // Allows letters to tighten when necessary
}
}

6. Accessible Layout

An accessible layout is crucial for users with motor skill impairments or those who use alternative input devices. Ensure that your app's user interface is designed with sufficient touch target size, making it easier for users to interact with buttons and controls.

Example:

import SwiftUI

struct AccessibleList: View {
var body: some View {
List {
ForEach(0..<10) { index in
Text("Item \(index)")
.padding()
.contentShape(Rectangle()) // Increase the tappable area for VoiceOver users
}
}
}
}

7. Testing with Assistive Technologies

Test your app's accessibility using assistive technologies such as VoiceOver, Switch Control, and Zoom. Put yourself in the shoes of users with disabilities to identify and fix potential accessibility issues.

Conclusion

In this blog, we've explored the key elements of creating accessible iOS apps using Swift and SwiftUI. By embracing accessibility APIs, supporting VoiceOver, implementing Dynamic Type, designing an accessible layout, and testing with assistive technologies, you can make your app inclusive and enrich the user experience for everyone. Prioritizing accessibility is not only a legal and ethical responsibility but also a great way to expand your app's user base and contribute to a more inclusive world.

HOW TO USE CORE ML IN SWIFT IOS APPS

Published: · Last updated: · 6 min read
Appxiom Team
Mobile App Performance Experts

Core ML is a framework provided by Apple that allows developers to integrate machine learning models into their iOS applications effortlessly. By leveraging the power of Core ML, developers can enhance their apps with intelligent features like image recognition, natural language processing, and more.

In this blog, we will explore the potential use cases of Core ML in Swift iOS apps and delve into the specific use case of image categorizations.

Use Cases where Core ML fits in

  • Image Recognition: Core ML enables the integration of pre-trained image recognition models into iOS apps. This can be utilized in applications such as augmented reality, object detection, and image classification.

  • Natural Language Processing: Core ML can process and analyze natural language, allowing developers to build applications with features like sentiment analysis, language translation, chatbots, and speech recognition.

  • Recommendation Systems: By leveraging Core ML, developers can build recommendation systems that provide personalized content, product recommendations, and suggestions based on user preferences and behavior.

  • Anomaly Detection: Core ML can be used to detect anomalies in data, enabling developers to build applications that identify unusual patterns or outliers in various domains such as fraud detection, network monitoring, and predictive maintenance.

  • Audio and Sound Analysis: Core ML's capabilities can be harnessed to analyze and process audio, enabling applications like voice recognition, speech synthesis, and music classification.

Using Core ML for Image Classification

To showcase how to use Core ML, we'll build an iOS app that uses Core ML to classify images. We'll leverage a pre-trained model called MobileNetV2, which can identify objects in images.

MobileNetV2 is a convolutional neural network architecture that is designed for mobile devices. It is based on an inverted residual structure, which allows it to achieve high performance while keeping the number of parameters and computational complexity low.

Let's get started!

Step 1: Set Up the Project

To start integrating Core ML into your Swift iOS app, follow these steps:

  • Launch Xcode and create a new project: Open Xcode and select "Create a new Xcode project" from the welcome screen or go to File → New → Project. Choose the appropriate template for your app (e.g., Single View App) and click "Next."

  • Configure project details: Provide the necessary details such as product name, organization name, and organization identifier for your app. Select the language as Swift and choose a suitable location to save the project files. Click "Next."

  • Choose project options: On the next screen, you can select additional options based on your project requirements. Ensure that the "Use Core Data," "Include Unit Tests," and "Include UI Tests" checkboxes are unchecked for this particular example. Click "Next."

  • Choose a location to save the project: Select a destination folder where you want to save your project and click "Create."

  • Import Core ML framework: In Xcode's project navigator, select your project at the top, then select your target under "Targets." Go to the "General" tab and scroll down to the "Frameworks, Libraries, and Embedded Content" section. Click on the "+" button and search for "CoreML.framework." Select it from the list and click "Add."

  • Add the MobileNetV2 model: To use the MobileNetV2 model for image classification, you need to add the model file to your project. Download the MobileNetV2.mlmodel file from a reliable source or create and train your own model using tools like Create ML or TensorFlow. Once you have the model file, simply drag and drop it into your Xcode project's file navigator. Ensure that the model file is added to your app's target by checking the checkbox next to your target name in the "Target Membership" section of the File Inspector panel.

  • Check Core ML compatibility: Verify that the Core ML model you're using is compatible with the version of Core ML framework you have imported. You can find the compatibility information in the Core ML model's documentation or the source from where you obtained the model.

With these steps completed, you have set up your Xcode project to integrate Core ML and are ready to move on to implementing the image classification logic using the MobileNetV2 model.

Step 2: Add the Core ML Model

Drag and drop the MobileNetV2.mlmodel file into your Xcode project. Ensure that the model file is added to your app's target.

Step 3: Create the Image Classifier

In your project, create a new Swift class called ImageClassifier. Import Core ML and Vision frameworks. Declare a class variable for the ML model:

import CoreML
import Vision

class ImageClassifier {
private let model = MobileNetV2()

// Image classification logic
}

Step 4: Implement the Image Classification Logic

Inside the ImageClassifier class, add a method called classifyImage that takes a UIImage as input and returns the classification results:

func classifyImage(_ image: UIImage, completion: @escaping (Result<[VNClassificationObservation], Error>) -> Void) {
guard let ciImage = CIImage(image: image) else {
completion(.failure("Failed to convert image to CIImage"))
return
}

let imageRequestHandler = VNImageRequestHandler(ciImage: ciImage)

do {
try imageRequestHandler.perform([createClassificationRequest(completion: completion)])
} catch {
completion(.failure(error))
}
}

private func createClassificationRequest(completion: @escaping (Result<[VNClassificationObservation], Error>) -> Void) -> VNCoreMLRequest {
let request = VNCoreMLRequest(model: model) { request, error in
guard let classifications = request.results as? [VNClassificationObservation] else {
completion(.failure("Failed to classify image"))
return
}

completion(.success(classifications))
}

return request
}

Step 5: Integrate the Image Classifier in your App

In your app's view controller or any other appropriate place, create an instance of the ImageClassifier class and call the classifyImage method to classify an image:

let imageClassifier = ImageClassifier()

func classify(image: UIImage) {
imageClassifier.classifyImage(image) { result in
switch result {
case .success(let classifications):
// Handle the classification results
print(classifications)
case .failure(let error):
// Handle the error
print(error)
}
}
}

Conclusion

Core ML empowers iOS developers to incorporate machine learning capabilities seamlessly into their Swift apps. In this blog, we explored the potential use cases of Core ML and focused on image classification as a specific example. By following the steps outlined above, you can integrate a pre-trained Core ML model, such as MobileNetV2, into your app and perform image classification with ease. Core ML opens up a world of possibilities for creating intelligent and engaging applications that cater to the needs of modern users.

Happy coding!

EXPLORING XCODE 15 BETA 3: BOOSTING IOS DEVELOPMENT EFFICIENCY

Published: · Last updated: · 4 min read
Don Peter
Cofounder and CTO, Appxiom

Being an iOS developer, it's essential to keep up with the latest tools and features to boost productivity and build outstanding apps. The recent launch of Xcode 15 beta 3 by Apple introduces numerous exciting features and improvements.

In this blog post, we'll delve into some of the significant enhancements introduced in this version and how they can empower developers to streamline their workflows, enhance app performance, and simplify localization efforts.

Expanded OS Support

Xcode 15 beta 3 supports the latest beta versions like iOS 17 beta 3, iPadOS 17 beta 3, visionOS 1 beta, macOS 14 beta 3, tvOS 17 beta 3, and watchOS 10 beta 3.

With the arrival of Xcode 15 beta 3, developers can now enjoy on-device debugging support for iOS 12 and later, tvOS 12 and later, and watchOS 4 and later. To take advantage of these features, it is necessary to have a Mac running macOS Ventura 13.4 or a more recent version.

Profiling Enhancements with Instruments 15

Xcode 15 beta 3 introduces Instruments 15, which includes a new RealityKit Trace template. This template equips developers with powerful profiling instruments for apps and games on visionOS.

The RealityKit Frames instrument provides a visual representation of frame rendering stages, while RealityKit Metrics helps identify rendering bottlenecks. With CoreAnimation statistics, 3D rendering statistics, and more, developers can diagnose and eliminate performance issues to deliver fluid and immersive experiences.

Xcode Cloud Enhancements

Xcode Cloud, Apple's continuous integration and delivery service, receives notable updates in Xcode 15 beta 3.

Developers can now benefit from continuous integration, enabling automatic building and testing of apps as code changes are made. Additionally, continuous delivery capabilities enable seamless deployment of apps to App Store Connect or TestFlight right after successful build and testing. These features simplify the app development process, ensuring faster iteration and feedback cycles.

Performance and Development Workflow Improvements

Xcode 15 beta 3 brings performance enhancements to expedite app development.

Faster build times empower developers to iterate and test their code more rapidly. Improved memory usage ensures that Xcode operates smoothly even with memory-intensive projects, enabling developers to focus on writing high-quality code without unnecessary interruptions.

Swift-C++/Objective-C++ Interoperability

With Xcode 15 beta 3, Swift now supports bidirectional interoperability with C++ and Objective-C++. This means developers can utilize a subset of C++ APIs in Swift and Swift APIs from C++. Enabling C++ interoperability via build settings opens up new possibilities for integrating existing codebases and leveraging the strengths of both languages.

For more details on the topic, please refer https://swift.org/documentation/cxx-interop

Accessibility Audit Support

To enhance app accessibility, Xcode 15 beta 3 introduces Accessibility Audit support. This automated check helps identify various accessibility issues within your app's views. By utilizing XCUIApplication().performAccessibilityAudit(), developers can proactively address missing labels, text scaling with Dynamic Type, and low contrast, ensuring their apps are accessible to a wider audience.

Streamlined Localization with String Catalogs

Xcode 15 beta 3 introduces String Catalogs (.xcstrings) as a file type for managing app localization. Developers can easily extract localizable strings from their source code, keeping String Catalogs in sync.

The native editor allows for efficient previewing and management of localized strings, simplifying the localization process and ensuring a smooth experience for international users.

Build System Enhancements with Explicit Modules

Xcode 15 beta 3 brings improvements to the build system, including a new mode called explicit modules. This opt-in feature enhances build performance, reliability, and correctness.

Developers can enable explicit modules by setting _EXPERIMENTAL_CLANG_EXPLICIT_MODULES as a user-defined build setting in C and Objective-C projects, which significantly improves the overall development experience.

Conclusion

Xcode 15 beta 3 introduces several groundbreaking features and improvements designed to enhance the iOS development experience. From advanced profiling tools to accelerated build times and streamlined localization, developers have an arsenal of resources at their disposal. Embracing these enhancements will empower developers to create exceptional apps that leverage the latest platform capabilities. As Xcode continues to evolve, developers can look forward to increased productivity and a more streamlined development process.

Happy coding!

BEST PRACTICES FOR MIGRATING FROM UIKIT TO SWIFTUI

Published: · Last updated: · 5 min read
Appxiom Team
Mobile App Performance Experts

As SwiftUI gains popularity, many iOS developers are considering migrating their existing UIKit-based projects to SwiftUI. This transition brings numerous benefits, including declarative syntax, automatic state management, and cross-platform development capabilities. However, migrating from UIKit to SwiftUI requires careful planning and execution to ensure a smooth and efficient transition.

In this blog, we will explore the best practices to employ while migrating from UIKit to SwiftUI and provide code examples to illustrate the process.

1. Understand SwiftUI Fundamentals

Before diving into migration, it is crucial to have a solid understanding of SwiftUI fundamentals. Familiarize yourself with SwiftUI's key concepts, such as views, modifiers, and the @State property wrapper. This knowledge will help you leverage SwiftUI's full potential during the migration process.

2. Identify the Migration Scope

Begin by identifying the scope of your migration. Determine which UIKit components, screens, or modules you intend to migrate to SwiftUI. Breaking down the migration process into smaller parts allows for easier management and testing. Start with simpler components and gradually move to more complex ones.

3. Start with New Features or Modules

Rather than migrating your entire UIKit project in one go, it is advisable to start by incorporating SwiftUI into new features or modules. This approach allows you to gain experience and evaluate SwiftUI's performance and compatibility within your existing codebase. Over time, you can expand the migration to encompass the entire project.

4. Leverage SwiftUI Previews

SwiftUI provides an excellent feature called "Previews" that allows you to see the real-time preview of your SwiftUI views alongside your code. Utilize this feature extensively during the migration process to visualize the changes and verify the desired behavior. SwiftUI previews facilitate rapid prototyping and make it easier to iterate on the design.

5. Convert UIKit Components

When migrating existing UIKit components to SwiftUI, aim for a step-by-step conversion rather than attempting to convert everything at once. Start by creating SwiftUI views that replicate the appearance and behavior of the UIKit components. Gradually refactor the code, replacing UIKit elements with SwiftUI equivalents, such as using Text instead of UILabel or Button instead of UIButton. As you progress, you can remove the UIKit code entirely.

6. Separate View and Data Logic

SwiftUI encourages a clear separation of view and data logic. Embrace this pattern by moving your data manipulation and business logic outside of the views. Use ObservableObject or StateObject to manage the data state separately. This approach enables better reusability, testability, and maintainability of your code.

7. Utilize SwiftUI Modifiers

SwiftUI modifiers provide a powerful way to apply changes to views. Take advantage of modifiers to customize the appearance, layout, and behavior of your SwiftUI views. SwiftUI's modifier chain syntax allows you to combine multiple modifiers and create complex layouts effortlessly.

8. Handle UIKit Interoperability

During the migration process, you may encounter situations where you need to integrate SwiftUI views with existing UIKit-based code. SwiftUI provides bridging mechanisms to enable interoperability. Use UIHostingController to embed SwiftUI views within UIKit-based view controllers, and UIViewControllerRepresentable to wrap UIKit views and view controllers for use in SwiftUI.

9. Maintain Code Consistency

Strive for consistency in your codebase by adopting SwiftUI conventions and best practices throughout the migration process. Consistent naming, indentation, and code structure enhance code readability and make collaboration easier. Additionally, consider utilizing SwiftUI's code organization patterns, such as SwiftUI App structuring, to keep your codebase well-organized.

10. Testing and Validation

Thoroughly test your SwiftUI code during and after migration. Ensure that the behavior and visual representation of the SwiftUI views match the original UIKit components. Use unit tests, integration tests, and UItesting frameworks like XCTest and SwiftUI's built-in testing tools to validate the functionality and behavior of your migrated code.

An Example

To illustrate the migration process, let's consider a simple example of migrating a UIKit-based login screen to SwiftUI.

UIKit Login Screen:

class LoginViewController: UIViewController {
private var usernameTextField: UITextField!
private var passwordTextField: UITextField!
private var loginButton: UIButton!

override func viewDidLoad() {
super.viewDidLoad()
// Initialize and configure UI components

usernameTextField = UITextField()
passwordTextField = UITextField()
loginButton = UIButton(type: .system)

// Add subviews and configure layout

view.addSubview(usernameTextField)
view.addSubview(passwordTextField)
view.addSubview(loginButton)

// Set up constraints// ...// Configure button action

loginButton.addTarget(self, action: #selector(loginButtonTapped), for: .touchUpInside)
}

@objc private func loginButtonTapped() {
// Handle login button tap event
let username = usernameTextField.text ?? ""
let password = passwordTextField.text ?? ""
// Perform login logic
}
}

SwiftUI Equivalent:

struct LoginView: View {
@State private var username: String = ""
@State private var password: String = ""
var body: some View {
VStack {
TextField("Username", text: $username)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()

SecureField("Password", text: $password)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()

Button(action: {
// Perform login logic
}) {
Text("Login")
.font(.headline)
.foregroundColor(.white)
.padding()
.background(Color.blue)
.cornerRadius(10)
}
.padding()
}
.padding()
}
}

In this example, we migrated the login screen from UIKit to SwiftUI. We replaced the UIKit components (UITextField and UIButton) with their SwiftUI counterparts (TextField and Button). We used the @State property wrapper to manage the text fields' state and implemented the login button action using SwiftUI's closure syntax.

Conclusion

Migrating from UIKit to SwiftUI opens up exciting possibilities for iOS developers, but it requires careful planning and execution. By understanding SwiftUI fundamentals, following the best practices mentioned in this blog, and leveraging the provided code examples, you can ensure a smooth and successful transition. Remember to start with smaller modules, utilize SwiftUI previews, separate view and data logic, and maintain code consistency throughout the migration process.

Happy migrating!

OBJECTIVE-C AND SWIFT - MY DECADE+ JOURNEY WITH IOS APP DEVELOPMENT

Published: · Last updated: · 6 min read
Appxiom Team
Mobile App Performance Experts

When I first started iOS development in 2010, the introduction of the iPad sparked my interest and motivation to dive into the world of app development. Objective-C was the primary language for iOS at the time, so it was crucial to understand its fundamentals. Initially, the syntax of Objective-C, with its square brackets and message-passing paradigm, felt unfamiliar and different from what I was accustomed to in other programming languages. However, with persistence and dedication, I began to grasp its unique concepts.

Objective-C's dynamic typing system was both a blessing and a challenge. It allowed for flexibility during runtime but also required careful consideration to ensure type safety. Understanding reference counting and memory management was another significant aspect to master, as it was crucial to avoid memory leaks and crashes.

Despite these challenges, Objective-C offered some advantages. One notable advantage was its extensive runtime, which allowed for dynamic behavior, runtime introspection, and method swizzling. This flexibility enabled developers to achieve certain functionalities that were not easily achievable in other languages. Additionally, the availability of a wide range of Objective-C libraries and frameworks, such as UIKit and Core Data, provided a solid foundation for iOS app development.

The Advantages of Objective-C

As I gained more experience with Objective-C, I began to appreciate its strengths. The extensive use of square brackets for method invocation, although initially confusing, provided a clear separation between method names and arguments. This clarity made code more readable, especially when dealing with complex method signatures.

Objective-C's dynamic nature also allowed for runtime introspection, which proved useful for tasks such as serialization, deserialization, and creating flexible architectures. Moreover, method swizzling, a technique enabled by Objective-C's runtime, allowed developers to modify or extend the behavior of existing classes at runtime. This capability was particularly helpful when integrating third-party libraries or implementing custom functionality.

Additionally, the Objective-C community was thriving, with numerous online resources, tutorials, and active developer forums. This vibrant ecosystem provided valuable support and knowledge-sharing opportunities, facilitating continuous learning and growth.

The Arrival of Swift

Embracing the Change In 2014, Apple introduced Swift, a modern programming language designed to replace Objective-C. Initially, there was some hesitation among developers, including myself, about Swift's adoption. Having invested considerable time in learning Objective-C, I wondered if transitioning to a new language would be worth the effort.

However, Swift's advantages quickly became apparent. Its concise syntax, built-in error handling, and type inference made code more expressive and readable. Swift's type safety features, including optionals and value types, reduced the likelihood of runtime crashes and enhanced overall stability.

During early Objective-C days one of the main challenges was the management of memory allocation. With the introduction of Automatic Reference Counting (ARC), it became much simpler and less prone to memory issues. ARC automated the process of deallocating unused objects, eliminating the need for manual memory management and reducing the risk of memory leaks and crashes. This shift reduced the cognitive burden associated with memory management in early days of Objective-C. And with Swift this burden got significantly alleviated.

Swift also introduced new language features such as generics, closures, and pattern matching, which enhanced code expressiveness and facilitated the implementation of modern programming paradigms, such as functional programming. These additions empowered developers to write cleaner, more maintainable code and allowed for better code reuse.

SwiftUI

A Paradigm Shift in iOS Development In 2019, Apple introduced SwiftUI, a declarative UI framework that marked a paradigm shift in iOS development. SwiftUI offered a radically different approach to building user interfaces, leveraging a reactive programming model and a live preview environment.

SwiftUI's declarative syntax allowed developers to define user interfaces as a series of state-driven views. The framework took care of managing the UI's state changes, automatically updating the views when the underlying data changed. This reactive nature eliminated the need for manual UI updates, making the code more concise and less prone to bugs.

Another significant advantage of SwiftUI was its live preview capabilities. Developers could see the changes they made to the UI in real-time, without needing to compile and run the app on a simulator or device. This instant feedback greatly accelerated the development process, allowing for rapid prototyping and iterative design.

Furthermore, SwiftUI's data binding and state management mechanisms simplified the handling of UI state. By leveraging the @State and @Binding property wrappers, developers could easily manage mutable state within the UI hierarchy, ensuring consistent and synchronized updates.

Embracing SwiftUI in Existing Projects

When SwiftUI was initially introduced, it was not yet mature enough to replace the entire UIKit ecosystem. Therefore, migrating existing projects from UIKit to SwiftUI required careful consideration and a pragmatic approach.

In my experience, I chose to adopt SwiftUI incrementally, starting with new features or screens while maintaining the existing UIKit codebase. This hybrid approach allowed me to leverage the power of SwiftUI gradually and mitigate any risks associated with migrating the entire project at once. It also provided an opportunity to evaluate SwiftUI's capabilities and assess its compatibility with existing functionality.

By embracing SwiftUI selectively, I could benefit from its strengths, such as its declarative syntax and reactive programming model, while still utilizing the well-established UIKit framework for certain complex or specialized components. As SwiftUI continued to evolve with each new iOS release, the compatibility gap between the two frameworks narrowed, enabling more extensive adoption of SwiftUI in existing projects.

And my journey continues

My journey from Objective-C to Swift and SwiftUI has been an exciting and transformative experience. While Objective-C laid the foundation for my iOS development career and provided invaluable knowledge of iOS frameworks, Swift and SwiftUI have revolutionized the way I approach app development.

Swift's modern syntax, safety features, and enhanced memory management have made code more robust and easier to maintain. The introduction of Swift enabled me to embrace modern programming paradigms and take advantage of powerful language features.

SwiftUI, with its declarative syntax, reactive programming model, and live preview capabilities, has changed the way I design and develop user interfaces. The shift from UIKit to SwiftUI has streamlined the development process, accelerated prototyping, and facilitated code reuse.

As iOS development continues to evolve, it is crucial to embrace new technologies and adapt to change. The experience of working with Objective-C and Swift expanded my skill set, and enabled me to architect and build Appxiom, a lightweight framework that detects bugs and performance issues in mobile apps.

HOW TO IMPLEMENT LIVE ACTIVITIES TO DISPLAY LIVE DATA IN DYNAMIC ISLAND IN IOS APPS

Published: · Last updated: · 4 min read
Don Peter
Cofounder and CTO, Appxiom

In today's fast-paced world, staying updated with the latest information is crucial. Whether it's live sports scores, breaking news, or real-time updates, having access to timely information can make a significant difference. That's where Live Activities in iOS come in.

With the ActivityKit framework, you can share live updates from your app directly on the Dynamic Island, allowing users to stay informed at a glance.

Live Activities not only provide real-time updates but also offer interactive functionality. Users can tap on a Live Activity to launch your app and engage with its buttons and toggles, enabling them to perform specific actions without the need to open the app fully.

Additionally, on the Dynamic Island, users can touch and hold a Live Activity to reveal an expanded presentation with even more content.

Implementing Live Activities in your app is made easy with the ActivityKit framework. Live Activities utilize the power of WidgetKit and SwiftUI for their user interface, providing a seamless and intuitive experience for users. The ActivityKit framework handles the life cycle of each Live Activity, allowing you to initialize and update a Live Activity with its convenient API.

Defining ActivityAttributes

We start by defining the data displayed by your Live Activity through the implementation of ActivityAttributes. These attributes provide information about the static data that is presented in the Live Activity. Additionally, ActivityAttributes are used to specify the necessary custom Activity.ContentState type, which describes the dynamic data of your Live Activity.

import Foundation
import ActivityKit


struct FootballScoreAttributes: ActivityAttributes {
public typealias GameStatus = ContentState


public struct ContentState: Codable, Hashable {
var score: String
var time: Int
...
}


var venue: Int
}

Creating Widget Extension

To incorporate Live Activities into the widget extension, you can utilize WidgetKit. Once you have implemented the necessary code to define the data displayed in the Live Activity using the ActivityAttributes structure, you should proceed to add code that returns an ActivityConfiguration within your widget implementation.

import SwiftUI
import WidgetKit


@main
struct FootballScoreActivityWidget: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: FootballScoreAttributes.self) { context in

} dynamicIsland: { context in
// Create the presentations that appear in the Dynamic Island.
// ...
}
}
}

If your application already provides widgets, you can incorporate the Live Activity by including it in your WidgetBundle. In case you don't have a WidgetBundle, such as when you offer only one widget, you should create a widget bundle following the instructions in the widget extension docs.

@main
struct FootballScoreWidgets: WidgetBundle {
var body: some Widget {
FootballScoreActivityWidget()
}
}

Adding a Widget Interface

Here, football score widget utilizes standard SwiftUI views to provide compact and minimal presentations.

import SwiftUI
import WidgetKit


@main
struct FootballWidget: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: FootballAttributes.self) { context in

} dynamicIsland: { context in

DynamicIsland {

} compactLeading: {
Label {
Text("Score \(context.attributes.score)")
} icon: {
Image(systemName: "score")
.foregroundColor(.indigo)
}
.font(.caption2)
} compactTrailing: {
Text("Time \(context.state.time)")
.multilineTextAlignment(.center)
.frame(width: 40)
.font(.caption2)
} minimal: {
VStack(alignment: .center) {
Image(systemName: "time")
Text("Time \(context.state.time)")
.multilineTextAlignment(.center)
.font(.caption2)
}
}
}
}
}

Initializing and Starting a Live Activity

The next step is to setup an initial state of the live activity and then call .request function to start the live activity.

if ActivityAuthorizationInfo().areActivitiesEnabled {

let initialContentState = FootballScoreAttributes.ContentState(score: "0", time:0)

let activityAttributes = FootballScoreAttributes(venue: venue)

let activityContent = ActivityContent(state: initialContentState, staleDate: Calendar.current.date(byAdding: .minute, value: 100, to: Date())!)

// Code to start the Live Activity.

scoreActivity = Activity.request(attributes: activityAttributes, content: activityContent)


}

Updating Live Activity Data

Now as the data changes, we need to update the content of the live activity. Use .update function to achieve the same.

let updatedScoreStatus = FootballScoreAttributes.GameStatus(score: score, time:time)

let alertConfiguration = AlertConfiguration(title: "Score Update", body: description, sound: .default)

let updatedContent = ActivityContent(state: updatedScoreStatus, staleDate: nil)

await scoreActivity?.update(updatedContent, alertConfiguration: alertConfiguration)

Conclusion

Now we have implemented Live Activities in our app and provided users with real-time updates and interactive functionality right in the Dynamic Island. With Live Activities, you can keep your users engaged and informed, enhancing their overall experience with your app.

INTEGRATING HASURA AND IMPLEMENTING GRAPHQL IN SWIFT-BASED IOS APPS USING APOLLO

Published: · Last updated: · 5 min read
Appxiom Team
Mobile App Performance Experts

Building robust and efficient iOS applications often involves integrating powerful backend services. Hasura, a real-time GraphQL engine, provides a convenient way to connect and interact with databases, enabling seamless integration between your iOS app and your backend.

In this tutorial, we will explore how to integrate Hasura and use GraphQL in Swift-based iOS apps. We will cover all CRUD operations (Create, Read, Update, Delete), as well as subscribing and unsubscribing to real-time updates.

Prerequisites

To follow this tutorial, you should have the following:

  • Xcode installed on your machine

  • Basic knowledge of Swift programming

  • Hasura GraphQL endpoint and access to a PostgreSQL database

1. Set Up Hasura and Database

Before we dive into coding, let's set up Hasura and Database:

1.1 Install Hasura CLI

Open a terminal and run the following command:

curl -L https://github.com/hasura/graphql-engine/raw/stable/cli/get.sh | bash

1.2 Initialize Hasura project

Navigate to your project directory and run:

hasura init hasura-app

1.3 Configure Hasura

Modify the config.yaml file generated in the previous step to specify your database connection details.

1.4 Apply migrations

Apply the initial migration to create the required tables and schema. Run the following command:

hasura migrate apply

1.5 Start the Hasura server

Run the following command:

hasura server start

2. Set Up the iOS Project

Now let's set up our iOS project and integrate the required dependencies:

  • Create a new Swift-based iOS project in Xcode.

  • Install Apollo GraphQL Client: Use CocoaPods or Swift Package Manager to install the Apollo iOS library. Add the following line to your Podfile and run pod install:

pod 'Apollo'
  • Create an ApolloClient instance: Open the project's AppDelegate.swift file and import the Apollo framework. Configure and create an instance of ApolloClient with your Hasura GraphQL endpoint.
import Apollo

// Add the following code in your AppDelegate.swift file
let apollo = ApolloClient(url: URL(string: "https://your-hasura-endpoint")!)

3. Perform CRUD Operations with GraphQL

Now we'll demonstrate how to perform CRUD operations using GraphQL in your Swift-based iOS app:

3.1 Define GraphQL queries and mutations

In your project, create a new file called GraphQL.swift and define the GraphQL queries and mutations you'll be using. For example:

import Foundation

struct GraphQL {
static let getAllUsers = """
query GetAllUsers {
users {
id
name
email
}
}
"""
static let createUser = """
mutation CreateUser($name: String!, $email: String!) {
insert_users_one(object: {name: $name, email: $email}) {
id
name
email
}
}
"""
static let updateUser = """
mutation UpdateUser($id: Int!, $name: String, $email: String) {
update_users_by_pk(pk_columns: {id: $id}, _set: {name: $name, email: $email}) {
id
name
email
}
}
"""
static let deleteUser = """
mutation DeleteUser($id: Int!) {
delete_users_by_pk(id: $id) {
id
name
email
}
}
"""
}

3.2 Fetch data using GraphQL queries

In your view controller, import the Apollo framework and make use of the ApolloClient to execute queries. For example:

import Apollo

class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()

apollo.fetch(query: GetAllUsersQuery()) {
result in
switch result {
case .success(let graphQLResult):
// Handle the result
if let users = graphQLResult.data?.users {
// Process the users data
}

case .failure(let error):
// Handle the error
print("Error fetching users: \(error)")
}
}
}
}

3.3 Perform mutations for creating/updating/deleting data

Use ApolloClient to execute mutations. For example:

// Create a user
apollo.perform(mutation: CreateUserMutation(name: "John", email: "john@example.com")) { result in
switch result {
case .success(let graphQLResult):
// Handle the result
if let user = graphQLResult.data?.insert_users_one {
// Process the newly created user
}

case .failure(let error):
// Handle the error
print("Error creating user: \(error)")
}
}

// Update a user
apollo.perform(mutation: UpdateUserMutation(id: 1, name: "Updated Name", email: "updated@example.com")) { result in
switch result {
case .success(let graphQLResult):
// Handle the result
if let updatedUser = graphQLResult.data?.update_users_by_pk {
// Process the updated user data
}

case .failure(let error):
// Handle the error
print("Error updating user: \(error)")
}
}

// Delete a user
apollo.perform(mutation: DeleteUserMutation(id: 1)) { result in
switch result {
case .success(let graphQLResult):
// Handle the result
if let deletedUser = graphQLResult.data?.delete_users_by_pk {
// Process the deleted user data
}

case .failure(let error):
// Handle the error
print("Error deleting user: \(error)")
}
}

4. Subscribe and Unsubscribe to Real-Time Updates

Hasura allows you to subscribe to real-time updates for specific data changes. Let's see how to do that in your iOS app:

4.1 Define a subscription

Add the subscription definition to your GraphQL.swift file. For example:

static let userAddedSubscription = """
subscription UserAdded {
users {
id
name
email
}
}
"""

4.2 Subscribe to updates

In your view controller, use ApolloClient to subscribe to the updates. For example:

swiftCopy code
let subscription = apollo.subscribe(subscription: UserAddedSubscription()) { result in
switch result {
case .success(let graphQLResult):
// Handle the real-time update
if let user = graphQLResult.data?.users {
// Process the newly added user
}

case .failure(let error):
// Handle the error
print("Error subscribing to user additions: \(error)")
}
}

4.3 Unsubscribe from updates

When you no longer need to receive updates, you can unsubscribe by calling the cancel method on the subscription object.

subscription.cancel()

Conclusion

In this tutorial, we learned how to integrate Hasura and use GraphQL in Swift-based iOS apps. We covered the implementation of CRUD operations (Create, Read, Update, Delete), as well as subscribing and unsubscribing to real-time updates.

By leveraging the power of Hasura and GraphQL, you can build responsive and efficient iOS apps that seamlessly connect with your backend services.

Happy coding!

MAXIMIZING EFFICIENCY IN IOS APP TESTING WITH BROWSERSTACK AND Appxiom

Published: · Last updated: · 4 min read
Appxiom Team
Mobile App Performance Experts

In today's rapidly evolving mobile app ecosystem, delivering a seamless user experience is crucial for success. To ensure high-quality iOS app performance, it's essential to have robust testing tools and frameworks in place.

This blog post explores the integration of BrowserStack and Appxiom, two powerful tools, to maximize the efficiency of iOS app testing. By leveraging their combined features, developers can identify and resolve performance issues, bugs, and other potential pitfalls more effectively.

Understanding BrowserStack

BrowserStack is a comprehensive testing platform that provides developers with a cloud-based infrastructure to test their applications on a wide range of real iOS devices. It offers an extensive device lab that includes the latest iPhone and iPad models, enabling thorough compatibility testing across various screen sizes, resolutions, and iOS versions. By utilizing BrowserStack, developers can ensure their iOS apps work seamlessly on different devices, reducing the risk of device-specific issues.

Introducing Appxiom

Appxiom is a lightweight tool available as an Android SDK and iOS framework. It offers valuable insights into the performance of iOS apps during both the QA and live phases. Appxiom helps detect performance issues such as memory leaks, abnormal memory usage, frame rate problems, app hangs, network call-related issues, function failures, and more. It generates detailed bug reports, including relevant data points that aid developers in reproducing and resolving bugs efficiently.

Integration Process

To maximize the efficiency of iOS app testing, follow these steps to integrate BrowserStack and Appxiom:

Step 1: Setting up BrowserStack

  • Create a BrowserStack account at https://www.browserstack.com/.

  • Familiarize yourself with BrowserStack's documentation and capabilities.

  • Install the required dependencies and configure your testing environment.

Step 2: Integrating Appxiom

  • Register with Appxiom using the 'Get Started' button in https://appxiom.com and login to dashboard.

  • Use "Add App" to link iOS application to Appxiom.

  • Integrate Appxiom framework to your application as explained in https://docs.appxiom.com.

  • Test your integration.

Step 3: Running Tests on BrowserStack

  • Utilize BrowserStack's extensive device lab to select the desired iOS devices for testing.

  • Configure your testing environment to run your iOS app on the chosen devices.

  • Implement test scripts or utilize existing test frameworks to automate your tests.

  • Execute tests on BrowserStack and observe the results.

Step 4: Analyzing Appxiom Reports

  • After running tests on BrowserStack, login to Appxiom dashboard.

  • Identify any performance issues, bugs, or abnormalities observed during the test.

  • Leverage Appxiom' detailed bug reports and data points to gain deeper insights into the detected issues.

  • Use the information provided by Appxiom to reproduce and fix bugs efficiently.

Benefits of Using BrowserStack and Appxiom Together for iOS App Testing

By combining BrowserStack and Appxiom, iOS app developers can experience the following benefits:

a) Enhanced Device Coverage

BrowserStack's device lab offers access to a wide range of real iOS devices, ensuring comprehensive compatibility testing. This reduces the risk of device-specific issues going unnoticed.

b) Efficient Bug Identification

Appxiom' advanced monitoring capabilities help detect performance issues and bugs in iOS apps. It provides detailed bug reports and data points, making it easier for developers to identify, reproduce, and fix issues quickly.

c) Reproducible Testing Environment

BrowserStack's cloud-based infrastructure ensures a consistent testing environment across multiple devices. This allows developers to replicate and verify bugs more accurately.

d) Streamlined Bug Resolution

By leveraging Appxiom' detailed bug reports, developers can understand the root cause of issues quickly. This accelerates the bug resolution process, leading to faster app improvements.

e) Time and Cost Savings

The integration of BrowserStack and Appxiom optimizes the iOS app testing workflow, reducing the time and effort required for testing and bug fixing. This ultimately leads to cost savings and improved time-to-market.

Conclusion

Using BrowserStack and Appxiom together offers a powerful combination of testing capabilities for iOS app development. By leveraging BrowserStack's extensive device lab and Appxiom' performance monitoring and bug detection features, developers can streamline their testing process, identify issues efficiently, and deliver high-quality iOS apps to users. Integrating these tools is a valuable strategy to maximize the efficiency of iOS app testing and ensure a seamless user experience in today's competitive mobile landscape.

Happy testing!

HOW TO INTEGRATE FIRESTORE WITH SWIFT AND HOW TO USE IT IN IOS APPS

Published: · Last updated: · 3 min read
Appxiom Team
Mobile App Performance Experts

Firebase Firestore is a cloud-based NoSQL database that allows you to store and retrieve data in real time. It is an excellent choice for iOS apps due to its ease of use, scalability, and security.

In this blog post, we will guide you through the process of integrating Firestore with Swift and demonstrate how to leverage its features in iOS development.

Adding Firebase to Your iOS Project

To begin, you need to add Firebase to your iOS project. Follow the instructions provided in the Firebase documentation (https://firebase.google.com/docs/ios/setup) to complete this step.

Once you have successfully added Firebase to your project, you must import the FirebaseFirestoreSwift framework. To do this, add the following line to your Podfile:

pod 'FirebaseFirestoreSwift'

Mapping Firestore Data to Swift Types

Firestore data is stored in documents, which are essentially JSON objects. You can map Firestore documents to Swift types by utilizing the Codable protocol.

To map a Firestore document to a Swift type, your type declaration should conform to Codable. Add the following two lines to your type declaration:

import Codable

@objc(MyDocument)struct MyDocument: Codable {
// ...
}

By adopting the Codable protocol, you gain access to a range of methods for encoding and decoding JSON objects. These methods will facilitate the reading and writing of data to Firestore.

Reading and Writing Data to Firestore

After successfully mapping your Firestore data to Swift types, you can commence reading and writing data to Firestore.

To read data from Firestore, utilize the DocumentReference class. This class offers several methods for obtaining, setting, and deleting data from Firestore documents.

For instance, the following code retrieves data from a Firestore document:

let document = Firestore.firestore().document("my-document")
let data = try document.data(as: MyDocument.self)

To write data to Firestore, make use of the setData() method on the DocumentReference class. This method accepts a dictionary of key-value pairs as its argument.

For example, the following code writes data to a Firestore document:

let document = Firestore.firestore().document("my-document")
document.setData(["name": "Robin", "age": 30])

Using Firestore in a Real-Time App

Firestore is a real-time database, meaning that any changes made to the data are instantly reflected across all connected clients. This real-time capability makes Firestore an ideal choice for developing real-time apps.

To incorporate Firestore into a real-time app, employ the Listener class. This class provides a mechanism for listening to changes in Firestore data.

For instance, the following code sets up a listener to monitor changes in a Firestore document:

let document = Firestore.firestore().document("my-document")
let listener = document.addSnapshotListener { snapshot, error inif let error = error {
// Handle the error
} else {
// Update the UI with new data
}
}

Conclusion

In this blog post, we explored the process of integrating Firestore with Swift and demonstrated its utilization in iOS development.

We hope this blog post has provided you with a solid foundation for working with Firestore in Swift.

Happy Coding!

GUIDE TO IMPLEMENT CONTINUOUS INTEGRATION (CI) AND CONTINUOUS DELIVERY (CD) FOR IOS APPS

Published: · Last updated: · 4 min read
Appxiom Team
Mobile App Performance Experts

In today's fast-paced software development world, it is essential to adopt efficient practices that enable continuous integration (CI) and continuous delivery (CD) to ensure the smooth and seamless development of iOS apps. CI/CD workflows automate the process of building, testing, and delivering software, allowing developers to iterate quickly and deliver high-quality applications.

This blog post will provide a high-level guide on implementing CI/CD for iOS apps, outlining the key concepts, tools, and best practices involved.

Understanding Continuous Integration and Continuous Delivery

Continuous Integration (CI) is a development practice that involves integrating code changes from multiple developers into a shared repository. It ensures that the changes are tested automatically and merged regularly, reducing integration issues and catching bugs early. Continuous Delivery (CD) extends CI by automating the release process, enabling rapid and frequent deployment of software updates.

Setting Up a CI/CD Environment

To implement CI/CD for iOS apps, you need to establish a dedicated CI/CD environment. This environment typically consists of a version control system, a build server, testing frameworks, and deployment tools. Consider using a cloud-based solution for scalability and ease of management.

Choosing a CI/CD Tool

Several CI/CD tools support iOS app development, including Jenkins, Travis CI, CircleCI, and Bitrise. Evaluate each tool based on factors like ease of setup, integration with version control systems, support for automated testing, scalability, and pricing.

Creating a Build Pipeline

A typical CI/CD workflow involves a series of steps in a build pipeline.

Here are the key components to consider:

1. Version Control and Branching Strategy

Use a version control system (e.g., Git) and adopt an appropriate branching strategy, such as GitFlow. This allows for effective collaboration, isolation of feature development, and bug fixing.

2. Build Configuration

Create a build configuration file (e.g., Xcode project or Fastlane) to define build settings, code signing details, and dependencies. Automate the build process to ensure consistency across environments.

3. Automated Testing

Leverage testing frameworks like XCTest or third-party tools such as EarlGrey or Quick/Nimble to create automated tests. Integrate these tests into your CI/CD pipeline to detect regressions and ensure the stability of your app.

4. Code Signing and Provisioning Profiles

Manage code signing identities and provisioning profiles for different environments (e.g., development, staging, and production). Use a secure and automated approach, such as Fastlane match or App Store Connect API, to simplify the code signing process.

Implementing Continuous Delivery

To achieve continuous delivery, automate the deployment process and streamline the release cycle. Consider the following aspects:

1. Deployment Automation

Automate the app deployment process using tools like Fastlane or custom scripts. This includes activities such as archiving the app, generating release notes, managing metadata, and uploading to distribution platforms.

2. App Store Release Process

Automate the release process to the App Store by leveraging tools like Fastlane's deliver or the App Store Connect API. This allows you to upload your app, submit it for review, and manage versioning and release notes seamlessly.

Monitoring and Analytics

Integrate monitoring and analytics tools, such as Firebase and Appxiom, into your CI/CD pipeline to track the performance and usage of your app. This helps in identifying issues and making data-driven decisions for future improvements.

Best Practices for CI/CD in iOS Apps

  • Ensure a comprehensive suite of automated tests to validate your app's functionality.

  • Use version control branches effectively to isolate features and bug fixes.

  • Store sensitive information (e.g., API keys, passwords) securely using environment variables or encrypted files.

  • Regularly update your CI/CD tools, dependencies, and frameworks to benefit from the latest features and security patches.

  • Implement a feedback loop to collect user feedback and iterate on your app's features and performance.

Conclusion

Implementing CI/CD for iOS apps streamlines the development, testing, and deployment processes, enabling faster iterations and high-quality releases. By automating tasks and integrating various tools, developers can focus more on building great apps while ensuring efficiency and reliability. Embracing CI/CD practices empowers developers to deliver feature-rich applications to users in a timely manner, while maintaining the highest standards of quality and performance.

HOW TO USE GENERICS IN SWIFT

Published: · Last updated: · 4 min read
Appxiom Team
Mobile App Performance Experts

Swift, Apple's modern programming language, offers a powerful feature called generics that greatly enhances code reusability, efficiency, and safety.

In this blog post, we will dive deep into generics and explore how they can be leveraged in iOS development. We will provide an overview of generics, demonstrate their usage with code examples, and highlight the benefits they bring to your iOS projects.

What are Generics?

Generics in Swift enable you to write flexible and reusable code that can work with different types of data. By using generics, you can create functions, classes, and structures that operate uniformly on a variety of types, avoiding code duplication and increasing maintainability.

How to Use Generics in Swift?

To utilize generics, you need to define a generic type or function. Let's start by examining generic types in Swift.

Generic Types:

A generic type can represent any specific type, allowing for maximum flexibility. Here's an example of a generic class called Stack that can store and manipulate a stack of elements of any type:

class Stack<T> {
var items = [T]()

func push(item: T) {
items.append(item)
}

func pop() -> T? {
return items.popLast()
}
}

In the code snippet above, we define a Stack class with a generic type parameter T. This parameter acts as a placeholder for any type that will be used with the Stack instance. The push function allows us to add elements to the stack, while the pop function removes and returns the topmost element from the stack.

Generic Functions:

Similarly, you can define generic functions that can work with different types. Let's look at an example of a generic function for swapping two values:

func swap<T>(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}

In this code snippet, the swap function is defined with a type parameter T using the placeholder <T>. The function takes in two parameters of the same type (a and b) and swaps their values using a temporary variable.

Advantages of Using Generics in iOS Development

Generics can be immensely beneficial in iOS development, offering increased code reuse, improved efficiency, and enhanced safety. Let's explore some practical use cases for leveraging generics in your iOS projects.

1. Reusable Code:

Generics enable you to create reusable code that can work with different data types. For example, consider a generic function that sorts an array of any type:

func sortArray&lt;T: Comparable&gt;(_ array: [T]) -&gt; [T] {
return array.sorted()
}

In this example, the sortArray function takes in an array of type T, constrained by the Comparable protocol to ensure elements can be compared. The function then returns the sorted array.

By using this generic function, you can sort arrays of integers, strings, or any other type that conforms to the Comparable protocol. This reusability saves you from writing separate sorting functions for each specific type.

2. Enhanced Efficiency:

Generics can also improve the efficiency of your code by eliminating the need for type casting. Consider a generic function that compares two values without explicitly specifying their types:

func compare&lt;T: Equatable&gt;(_ a: T, _ b: T) -&gt; Bool {
return a == b
}

In this case, the compare function takes two parameters of type T, constrained by the Equatable protocol, which ensures that values can be equated using the == operator. The function then compares the two values and returns a Boolean result.

By using this generic function, you can compare values of any type that conforms to the Equatable protocol without the overhead of type casting, resulting in more efficient code execution.

3. Type Safety:

Generics contribute to improved type safety by catching potential errors at compile time. With generics, the Swift compiler ensures that you only operate on valid types and prevents type-related issues that might arise at runtime.

Conclusion

Generics in Swift provide a powerful toolset for creating flexible and reusable code in iOS development. By leveraging generics, you can build more efficient and maintainable applications, enhance code reuse, and ensure type safety. Understanding and effectively utilizing generics will undoubtedly elevate your iOS development skills and improve the quality of your code.

Happy Coding!