Writing about
mobile apps
Technical articles about iOS development, Swift, SwiftUI, and the mobile ecosystem.
Using non-breaking space in Swift or how to prevent an automatic line break
If you want a better control in line breaking inside UILabel or UITextView you can use a no-break space (NBSP).
If you want a better control in line breaking inside UILabel or UITextView you can use a no-break space that you may also know as non-breakable space (NBSP). In Swift, this is a special character and it is shown like this:
\u{00a0}
So let's imagine that you have for example a US domestic phone number like (555) 123–4567 and you have to display it inside a UILabel along with a text.
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Without non-breaking space - phone number may break across lines
label.text = "Please call us at (555) 123-4567 for more information"
}
}
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// With non-breaking space - keeps phone number together
let phoneNumber = "(555)\u{00a0}123–4567"
label.text = "Please call us at \(phoneNumber) for more information"
}
}
Common Use Cases
Non-breaking spaces are particularly useful for:
- Phone numbers: Keep area codes and numbers together
- Measurements: Keep numbers with their units (100 kg, 50 mph)
- Names with titles: Keep titles with names (Dr. Smith, Mr. Johnson)
- Currency: Keep currency symbols with amounts ($29.99, €15.00)
- Time formats: Keep time components together (3:30 PM)
The non-breaking space character (\u{00a0}) ensures that text elements that should stay together won't be separated by automatic line breaks, giving you better control over text layout in your iOS applications.
Symbolicate a line from a raw crash log on iOS
When your app crashes, it is never a good thing, but hopefully, you have ways to understand what happens. Learn how to symbolicate crash logs manually.
When your app crashes, it is never a good thing, but hopefully, you have ways to understand what happens. If you upload your app directly through Xcode you can find the crashes directly from the organizer window, symbolicate it and even open the line that crashed inside your project. You can also use some crash reporting tools that will do all the work for you.
But sometimes, especially if you work on an SDK, you may receive a crash log by email or sometimes only just the line where the crash happens.
Raw Crash Log Example
Crashed: com.apple.main-thread
0 libswiftCore.dylib 0x3dcd08 swift_isUniquelyReferenced_nonNull_native + 38
1 YourSDK 0x63974 _hidden#10814_ + 36 (__hidden#11065_:36)
2 YourSDK 0x72394 _hidden#10850_ + 80 (__hidden#11065_:80)
3 YourSDK 0x1834c _hidden#10852_ + 4395545212 (__hidden#593_:4395545212)
Symbolicating the Crash
Here the crash is caused by the SDK, but right now it is impossible to know what line of the code generated the crash. There are 3 lines that are really important for us to decrypt: 1, 2, and 3. To symbolicate these lines we need the .dSYM of our SDK. Hopefully when you archive your project Xcode generates the .dSYM file for you.
We are going to use the following command line atos -arch arm64 -o followed by the path of the DWARF file contained inside the .dSYM archive:
atos -arch arm64 -o YOURSDK.framework.dSYM/Contents/Resources/DWARF/YOURSDK 0x63974
The output may look like this:
YourClassThatCrash.theMethodThatCrash() (in YOURSDK) (YourClassThatCrash.swift:74)
With this it should be much easier to find the crash, correct it and make your users happy.
What's new for AdTech announced during the WWDC 2022
What Apple presented about AdTech: SKAdNetwork 4.0, Ads with SharePlay, Pasteboard access, Location attribution…
Apple released some videos relevant to the Ad Tech industry at WWDC 2022. At Teads, we take very seriously the fact of being up to date with new technologies. Here's a summary of the key announcements.
New Platform Updates
With iOS 16, there are new features about privacy and security:
Location Attribution
The name of the app using the user location will now appear in conjunction with the location symbol system-wide — if an app uses your location, you will know which app, even outside the app.
Pasteboard Access
To access the shared clipboard or simply paste the content from another app, you now have to ask permission. Apple offers controls that allow the user to paste the content of the clipboard with a tap without requiring a full permission prompt.
Device Name Entitlement
Before iOS 16, an app could access the device name (e.g., "Antoine's iPhone"). Now UIDevice.name will return just the model name (iPhone / iPad). You can still access the user-assigned name using a special entitlement, but sharing it with third parties is not permitted.
App Tracking Transparency
For Apple, privacy is a fundamental human right. Tracking is the fact of linking user or device data collected inside your app with data collected from others (apps, websites, offline data) for targeting ads or advertising measurement. If the user denies ATT, you can still gather information for internal use but cannot share it with third parties.
SKAdNetwork 4.0
SKAdNetwork is Apple's privacy-preserving ad-attribution system. Version 4.0 introduces:
Crowd Anonymity
A method to pass information to the ad network depending on the number of installations. If the app has few installations the attribution data sent will be limited. With more installations, more data will be sent. There are 3 levels: Low, Medium, and High.
New Attribution Values
The source identifier replaces the campaign field and expands from two to four digits. The conversion value is split into a fine-grained value (6-bit, 0-63) and a coarse-grained value (low, medium, high). Which value is sent depends on the crowd anonymity level.
// Conversion value split
enum CoarseGrainedValue {
case low
case medium
case high
}
// Fine-grained value: 6-bit value (0-63)
let fineGrainedValue: Int = 42
// Only one value is sent based on crowd anonymity level
Web Attribution
It is now possible to use SKAdNetwork for ads on the Web (Safari only) for ads that advertise an App Store page.
Apple's Privacy Pillars
- Data minimization — use only the data you need
- On-device processing — do not send sensitive data to your server
- Transparency and control — explain what data you are using
- Security protections — make sure the data transiting is protected
Adding Dark Mode to your iOS app to make your app bright
With iOS 13 Apple added a dark mode to its operating system. Learn how to implement it in your app.
With iOS 13 Apple added a dark mode to its operating system. Almost two months after the launch there are only few apps that already implement it. I will try to show you how you can implement it in your app and make your users happiest.
Using Apple Dark Mode Compatible Colors
Apple shipped basic system colors with iOS 13. Instead of UIColor.blue, you can use UIColor.systemBlue that gives you a different blue on light and dark mode without any effort.
if #available(iOS 13, *) {
view.backgroundColor = .systemBlue
} else {
view.backgroundColor = .blue
}
Two other useful colors: UIColor.label (black on light, white on dark) and UIColor.systemBackground (white on light, black on dark).
Creating Custom Colors in Code
With iOS 13, Apple added initWithDynamicProvider for UIColor, which lets you access the current traitCollection and know if the user is using dark mode.
Creating the AppColor Protocol
protocol AppColor {
var light: UIColor { get }
var dark: UIColor { get }
var fallbackColorBeforeiOS13: UIColor { get }
func color() -> UIColor
}
extension AppColor {
func color() -> UIColor {
if #available(iOS 13, *) {
return UIColor.init { (trait) -> UIColor in
return trait.userInterfaceStyle == .dark ? self.dark : self.light
}
}
return self.fallbackColorBeforeiOS13
}
}
Creating an Enum of AppColors
enum CustomColors: AppColor {
case primary, secondary
var light: UIColor {
switch self {
case .primary: return UIColor(red: 0, green: 0.8, blue: 0.69, alpha: 1)
case .secondary: return UIColor(red: 219/255, green: 230/255, blue: 228/255, alpha: 1.0)
}
}
var dark: UIColor {
switch self {
case .primary: return light
case .secondary: return UIColor(red: 0, green: 205/255, blue: 177/255, alpha: 0.3)
}
}
var fallbackColorBeforeiOS13: UIColor {
return light
}
}
LaunchScreen & Images
For the launch screen, create a new color set in your assets catalog and enable dark mode appearance. For images, the process is the same: enable dark mode on the image asset and provide dark appearance variants. Your app will automatically load the correct variant based on the user's appearance setting.
Apple made it really easy for developers to implement dark mode, so we should — if we care about our users.
How to make a simple Countdown Timer with SwiftUI
Step-by-step instructions on how to build a countdown timer using the SwiftUI framework.
Apple launched SwiftUI, a framework that allows us to make app interfaces in a 100% swifty way. Here's how to make a countdown timer.
Pre-Requirements
To start with SwiftUI, download the latest build of Xcode 11 and macOS Catalina 10.15. SwiftUI is only available on iOS 13, macOS 10.15, watchOS 6 and iPadOS 13.
Making the Timer Logic
To refresh our interface every second, we use a timer and declare a date for our countdown:
import SwiftUI
struct ContentView: View {
@State var date = Date()
let referenceDate = Date()
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
Text("Hello World")
.onReceive(timer) { input in
date = input
}
}
}
Note here the @State recreates our interface every time the date is updated.
Adding Countdown Logic
func countDownString(from referenceDate: Date, until date: Date) -> String {
let calendar = Calendar(identifier: .gregorian)
let components = calendar.dateComponents([.day, .hour, .minute, .second],
from: referenceDate,
to: date)
return String(format: "%02d:%02d:%02d:%02d",
components.day ?? 0,
components.hour ?? 0,
components.minute ?? 0,
components.second ?? 0)
}
The Complete Code
import SwiftUI
struct ContentView: View {
@State var date = Date()
let referenceDate = Date()
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
VStack {
Text("Countdown Timer")
.font(.largeTitle)
.padding()
Text(countDownString(from: referenceDate, until: date))
.font(.system(size: 40, design: .monospaced))
.padding()
.onReceive(timer) { input in
date = input
}
.onAppear(perform: {
date = Date()
})
}
}
func countDownString(from referenceDate: Date, until date: Date) -> String {
let calendar = Calendar(identifier: .gregorian)
let components = calendar.dateComponents([.day, .hour, .minute, .second],
from: referenceDate,
to: date)
return String(format: "%02d:%02d:%02d:%02d",
components.day ?? 0,
components.hour ?? 0,
components.minute ?? 0,
components.second ?? 0)
}
}
This creates a simple countdown timer that updates every second using SwiftUI's @State property wrapper and the Timer.publish method.