Home All About iOS UUID (Swift/iOS ≥ 6)
Post
Cancel

All About iOS UUID (Swift/iOS ≥ 6)

All About iOS UUID (Swift/iOS ≥ 6)

iPlayground 2018 Recap & All About UUID

Introduction:

Last Saturday and Sunday, I attended the iPlayground Apple software developer conference. This event was recommended by a colleague, and I wasn’t familiar with it before attending.

Over the two days, the event and schedule were smooth, and the agenda included:

  1. Fun topics: Bicycles, Decaying Code, Evolution of iOS/API, Where’s Willy (CoreML Vision)
  2. Practical topics: Testing (XCUITest, Dependency Injection), Alternative animation effects with SpriteKit, GraphQL
  3. Advanced topics: In-depth Swift analysis, iOS Jailbreaking/Tweak development, Redux

The Bicycle Project left a deep impression. Using an iPhone as a sensor to detect bicycle pedal rotation, the presenter switched slides while riding a bicycle on stage (the main goal was to create an open-source version of Zwift, sharing many pitfalls such as Client/Server communication, latency issues, and magnetic interference).

Decaying Dirty Code; it resonated deeply, bringing a knowing smile. Technical debt accumulates this way: rushed development schedules lead to quick but poorly structured solutions, and subsequent developers don’t have time to refactor, causing the debt to pile up. Eventually, the only solution might be to start over.

Testing (Design Patterns in XCUITest) by a senior from KKBOX was very open, sharing their methods, code examples, encountered issues, and solutions. This session was particularly beneficial for our work. Testing is an area I’ve always wanted to strengthen, and now I can study it thoroughly.

Listening to the Lighting Talk made me want to share too 😂. I’ll prepare better next time!

The official party afterward was sincere, with great food and drinks. Listening to the seniors’ heartfelt words was both relaxing and informative, enhancing many soft skills.

NTU Backstage Cafe

NTU Backstage Cafe

I learned that this was the first edition, and I was truly honored to participate. Kudos to all the staff and speakers!

The purpose of attending conferences is to: broaden horizons, absorb new knowledge, understand the ecosystem, and explore areas you wouldn’t normally encounter, and deepen expertise, by identifying any overlooked aspects or discovering new methods in familiar areas.

I took many notes to study and savor later.

All About UUID

After the conference, I immediately applied what I learned to our app. This session was led by senior Zonble, who has been writing from iPhone OS 2 to iOS 12, which is impressive. I started from iOS 11/Swift 4, so I missed the turbulent times when Apple changed APIs.

It’s reasonable that UUIDs went from accessible to restricted. If used for good purposes: identifying user devices, advertising, or third-party operations, it can be beneficial. But if misused, it can track and profile users (e.g., knowing you often travel, have kids, and live in Taipei based on installed apps like travel, Taipei bus, BMW, and baby care apps). Combined with personal data entered in apps, the potential misuse is concerning.

However, this also affects many legitimate users. Using UUIDs for user data decryption keys or device identification is significantly impacted. I admire the engineers of that era; the impact would have caused complaints from bosses and users, requiring quick alternative solutions.

Alternatives:

This article focuses on obtaining UUIDs to identify unique devices. For alternatives to knowing which apps a user has installed, consider these keywords: UIPasteboard pasteboardWithName: create: (using the clipboard to share between apps), canOpenURL: info.plist LSApplicationQueriesSchemes (using canOpenURL to check if an app is installed, listing up to 50 entries in info.plist)

  1. Using MAC Address as UUID, but this was also banned later.
  2. Finger Printing (Canvas/User-Agent…): Not researched, but mainly used to generate the same UUID for Safari and apps, Deferred Deep Linking. AmIUnique?
  3. ID entifier F or V endor (IDFV): Currently the mainstream solution 🏆. The concept is that Apple generates a UUID for the user based on the Bundle ID prefix. The same Bundle ID prefix will generate the same UUID, e.g., com.518.work/com.518.job will get the same UUID on the same device. As the name suggests, ID For Vendor, Apple considers apps with the same prefix as from the same vendor, so sharing UUIDs is allowed.

ID entifier F or V endor (IDFV):

1
let DEVICE_UUID:String = UIDevice.current.identifierForVendor?.uuidString ?? UUID().uuidString

Note: When all apps from the same vendor are removed and then reinstalled, a new UUID will be generated ( if both com.518.work and com.518.job are deleted, and then com.518.work is reinstalled, a new UUID will be generated ) Similarly, if you have only one app, deleting and reinstalling it will generate a new UUID

Due to this characteristic, our company’s other apps use Key-Chain to solve this problem. After listening to the advice of experienced speakers, we have verified that this approach is correct!

The process is as follows:

When the Key-Chain UUID field has a value, retrieve it; otherwise, get the UUID value of IDFA and write it back

When the Key-Chain UUID field has a value, retrieve it; otherwise, get the UUID value of IDFA and write it back

Key-Chain writing method:

1
2
3
4
5
6
7
8
9
if let data = DEVICE_UUID.data(using: .utf8) {
    let query = [
        kSecClass as String       : kSecClassGenericPassword as String,
        kSecAttrAccount as String : "DEVICE_UUID",
        kSecValueData as String   : data ] as [String : Any]
    
    SecItemDelete(query as CFDictionary)
    SecItemAdd(query as CFDictionary, nil)
}

Key-Chain reading method:

1
2
3
4
5
6
7
8
9
10
11
let query = [
    kSecClass as String       : kSecClassGenericPassword,
    kSecAttrAccount as String : "DEVICE_UUID",
    kSecReturnData as String  : kCFBooleanTrue,
    kSecMatchLimit as String  : kSecMatchLimitOne ] as [String : Any]

var dataTypeRef: AnyObject? = nil
let status: OSStatus = SecItemCopyMatching(query as CFDictionary, &dataTypeRef)
if status == noErr,let dataTypeRef = dataTypeRef as? Data,let uuid = String(data:dataTypeRef, encoding: .utf8) {
   //uuid
} 

If you find Key-Chain operations too cumbersome, you can encapsulate them yourself or use third-party libraries.

Complete CODE:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
let DEVICE_UUID:String = {
    let query = [
        kSecClass as String       : kSecClassGenericPassword,
        kSecAttrAccount as String : "DEVICE_UUID",
        kSecReturnData as String  : kCFBooleanTrue,
        kSecMatchLimit as String  : kSecMatchLimitOne ] as [String : Any]
    
    var dataTypeRef: AnyObject? = nil
    let status: OSStatus = SecItemCopyMatching(query as CFDictionary, &dataTypeRef)
    if status == noErr,let dataTypeRef = dataTypeRef as? Data,let uuid = String(data:dataTypeRef, encoding: .utf8) {
        return uuid
    } else {
        let DEVICE_UUID:String = UIDevice.current.identifierForVendor?.uuidString ?? UUID().uuidString
        if let data = DEVICE_UUID.data(using: .utf8) {
            let query = [
                kSecClass as String       : kSecClassGenericPassword as String,
                kSecAttrAccount as String : "DEVICE_UUID",
                kSecValueData as String   : data ] as [String : Any]
        
            SecItemDelete(query as CFDictionary)
            SecItemAdd(query as CFDictionary, nil)
        }
        return DEVICE_UUID
    }
}()

Because I need to reference it in other Extension Targets, I directly wrapped it into a closure parameter for use.

If you have any questions or feedback, feel free to contact me.

===

本文中文版本

===

This article was first published in Traditional Chinese on Medium ➡️ View Here


This post is licensed under CC BY 4.0 by the author.

Enhance User Experience by Adding 3D TOUCH to Your iOS APP (Swift)

What? iOS 12 Can Receive Push Notifications Without User Authorization (Swift)