Search code examples
swiftstructswift-optionals

CompactMapValues not working on this simple dictionary


I have a model that looks like:

public struct Profile {
  public let bio: String?
  public let company: String
  public let createdDate: Date
  public let department: String
  public let email: String
  public let firstName: String
  public let coverImageURL: URL?
  public let jobTitle: String
  public let lastName: String
  public let location: String?
  public let name: String
  public let profileImageURL: URL?
  public let roles: [String]
  public let isActive: Bool
  public let updatedDate: Date?
  public let userID: String

  public init(
    bio: String?, company: String, createdDate: Date, department: String, email: String, firstName: String, coverImageURL: URL?, jobTitle: String,
    lastName: String, location: String?, name: String, profileImageURL: URL?, roles: [String], isActive: Bool, updatedDate: Date?, userID: String) {
    self.bio = bio
    self.company = company
    self.createdDate = createdDate
    self.department = department
    self.email = email
    self.firstName = firstName
    self.coverImageURL = coverImageURL
    self.jobTitle = jobTitle
    self.lastName = lastName
    self.location = location
    self.name = name
    self.profileImageURL = profileImageURL
    self.roles = roles
    self.isActive = isActive
    self.updatedDate = updatedDate
    self.userID = userID
  }


}

extension Profile: Equatable { }

I am trying to create a tuple that represents it as (model: Profile, json: [String: Any])

using the following method -

  func makeProfile() -> (model: Profile, json: [String: Any]) {

    let updatedDate = Date()
    let updatedDateStr = updatedDate.toString(dateFormat: "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX")
    let createdDate = Date()
    let createdDateStr = createdDate.toString(dateFormat: "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX")

    let coverImageURL = makeURL("https://headerUri.com")
    let profileImageURL = makeURL("https://pictureUri.com")
    let model = Profile(
      bio: "Some Bio",
      company: "Cool Job INC",
      createdDate: createdDate,
      department: "Engineering",
      email: "[email protected]",
      firstName: "Anne",
      coverImageURL: coverImageURL,
      jobTitle: "Test Dummy",
      lastName: "Employee",
      location: "London",
      name: "Anne Employee",
      profileImageURL: profileImageURL,
      roles: ["ADMIN"],
      isActive: true,
      updatedDate: updatedDate,
      userID: UUID().uuidString
    )

    let json: [String: Any] = [
      "bio": model.bio,
      "company": model.company,
      "createdDate": createdDateStr,
      "department": model.department,
      "email": model.email,
      "firstName": model.firstName,
      "headerUri": model.coverImageURL?.absoluteString,
      "jobTitle": model.jobTitle,
      "lastName": model.lastName,
      "location": model.location,
      "name": model.name,
      "pictureUri": model.profileImageURL?.absoluteString,
      "roles": model.roles,
      "isActive": model.isActive,
      "updatedDate": updatedDateStr,
      "userId": model.userID
      ].compactMapValues { $0 }

    return (model: model, json: json)
  }
}

extension Date {
  func toString(dateFormat format: String) -> String {
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = format
    return dateFormatter.string(from: self)
  }
}

All of the optional properties are showing a warning Expression implicitly coerced from 'String?' to 'Any'

enter image description here

I have added .compactMapValues { $0 } to the dict but has had no effect.

How can I clear this warning?


Solution

  • You're getting an error because you trying to implicitly coerce an optional value (which could be nil) to Any, which hides that the value could be nil.

    compactMapValues doesn't guarantee at compile-time that the value is not nil; all the compiler knows is that something that is an optional (like String?) is now treated as a non-optional Any.

    One way you could avoid the error is by providing a default value instead of the optional:

    let json: [String: Any] = [
       "bio": model.bio ?? ""
        // ...
       ]
    

    Alternatively, you can create a dictionary of: [String, Any?] to assign values to, and then use .compactMapValues, which would give you back non-optional values of Any.

    let withOptionals: [String: Any?] = [
       "bio": model.bio,
       // ...
    ]
    
    let json: [String, Any] = withOptionals.compactMapValues { $0 }